<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <title>Hexo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description">
<meta property="og:type" content="website">
<meta property="og:title" content="Hexo">
<meta property="og:url" content="http://yoursite.com/page/7/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Hexo">
<meta name="twitter:description">
  
    <link rel="alternative" href="/atom.xml" title="Hexo" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.png">
  
  <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  <link rel="stylesheet" href="/css/style.css" type="text/css">
  

</head>
<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">Hexo</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" results="0" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://yoursite.com"></form>
      </div>
    </div>
  </div>
</header>
      <div class="outer">
        <section id="main">
  
    <article id="post-nio/13.QuickServer概述" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/02/nio/13.QuickServer概述/" class="article-date">
  <time datetime="2016-01-02T05:00:00.000Z" itemprop="datePublished">2016-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/02/nio/13.QuickServer概述/">QuickServer概述</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　QuickServer是一个免费的开源Java库，用于快速创建健壮的多线程、多客户端TCP服务器应用程序。使用QuickServer，用户可以只集中处理应用程序的逻辑/协议，从而方便的建立功能强大的服务器应用。该程序由Akshathkumar Shetty设计和实现。</p>
<p>参考资料：<br><a href="http://blog.csdn.net/lizhi200404520/article/details/6700532" target="_blank" rel="external">http://blog.csdn.net/lizhi200404520/article/details/6700532</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/02/nio/13.QuickServer概述/" data-id="cijzzpv7r00621cfs2cib5m29" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/xSocket/">xSocket</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/12.xSocket系列五之Multiplexed" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/02/nio/12.xSocket系列五之Multiplexed/" class="article-date">
  <time datetime="2016-01-02T04:00:00.000Z" itemprop="datePublished">2016-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/02/nio/12.xSocket系列五之Multiplexed/">xSocket系列五之Multiplexed</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　xsocket Multiplexed是xSocket的一个扩展模块，xsocket Multiplexed的定义是支持一个物理TCP连接之上运行的多个逻辑连接。<br>说白了其实就是长连接的利用，所谓长连接就是Client方与Server方先建立通讯连接，连接建立后不断开， 然后再进行报文发送和接收。对于点对点的通讯，比较适合。使用xsocket Multiplexed进行持久连接通讯，对业务开发更加友好。 </p>
<p>参考资料：<br><a href="http://fengshihao.iteye.com/blog/670258" target="_blank" rel="external">http://fengshihao.iteye.com/blog/670258</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/02/nio/12.xSocket系列五之Multiplexed/" data-id="cijzzpv7u00661cfs96nfu9ia" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/xSocket/">xSocket</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/11.xSocket系列四之xlightweb" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/02/nio/11.xSocket系列四之xlightweb/" class="article-date">
  <time datetime="2016-01-02T03:00:00.000Z" itemprop="datePublished">2016-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/02/nio/11.xSocket系列四之xlightweb/">xSocket系列四之xlightweb</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　xlightweb是基于xsockets的使用了NIO的框架，通过它我可以构建同步阻塞的或者异步非阻塞的http客户端和http服务器。</p>
<p>参考资料：<br><a href="http://fengshihao.iteye.com/blog/670258" target="_blank" rel="external">http://fengshihao.iteye.com/blog/670258</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/02/nio/11.xSocket系列四之xlightweb/" data-id="cijzzpv7x006a1cfso4teuxx6" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/xSocket/">xSocket</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/10.xSocket系列三之源码分析二" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/02/nio/10.xSocket系列三之源码分析二/" class="article-date">
  <time datetime="2016-01-02T02:00:00.000Z" itemprop="datePublished">2016-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/02/nio/10.xSocket系列三之源码分析二/">xSocket系列三之源码分析二</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　这篇文章我们将重点关注一些功能点的实现</p>
<h2 id="ConnectionManager"><a href="#ConnectionManager" class="headerlink" title="ConnectionManager"></a>ConnectionManager</h2><p>　　我们采用xSocket在服务端和客户端之间建立了长连接，这样就会出现一个问题：长连接什么时候关闭呢？如果通信长时间没有发生，即连接处于空闲状态，需要关闭连接。这里xScoket为我们提供了一个参数，可以设置每一个长连接的最大空闲时间！在xSocket中，该功能主要由ConnectionManager完成。它主要是对连接最大时长超时、连接空闲时长超时进行管理，由Server的ConnectionManager成员进行管理，其维护了一个connection的链表。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ConnectionManager</span> </span>&#123;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> ArrayList&lt;WeakReference&lt;TimeoutMgmHandle&gt;&gt; handles = <span class="keyword">new</span> ArrayList&lt;WeakReference&lt;TimeoutMgmHandle&gt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> WachdogTask conCheckWatchDogTask = <span class="keyword">null</span>;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">int</span> watchDogRuns;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">int</span> countIdleTimeouts;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">int</span> countConnectionTimeouts;</span><br><span class="line">	</span><br><span class="line">	<span class="keyword">private</span> AtomicInteger currentSize = <span class="keyword">new</span> AtomicInteger(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">public</span> TimeoutMgmHandle <span class="title">register</span><span class="params">(NonBlockingConnection connection)</span> </span>&#123;</span><br><span class="line">		TimeoutMgmHandle mgnCon = <span class="keyword">new</span> TimeoutMgmHandle(connection);</span><br><span class="line">		</span><br><span class="line">		WeakReference&lt;TimeoutMgmHandle&gt; ref = mgnCon.getWeakRef();</span><br><span class="line">		<span class="keyword">if</span> (ref != <span class="keyword">null</span>) &#123;</span><br><span class="line">		    <span class="keyword">synchronized</span> (handles) &#123;</span><br><span class="line">		        handles.add(ref);</span><br><span class="line">		    &#125;</span><br><span class="line">		&#125;</span><br><span class="line">		currentSize.incrementAndGet();</span><br><span class="line">		<span class="keyword">return</span> mgnCon;</span><br><span class="line">	&#125;</span><br><span class="line">    </span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">updateTimeoutCheckPeriod</span><span class="params">(<span class="keyword">long</span> requiredMinPeriod)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// if non watchdog task already exists and the required period is smaller than current one -&gt; return</span></span><br><span class="line">        <span class="keyword">if</span> ((conCheckWatchDogTask != <span class="keyword">null</span>) &amp;&amp; (watchDogPeriodConCheckMillis &lt;= requiredMinPeriod)) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// set watch dog period</span></span><br><span class="line">        watchDogPeriodConCheckMillis = requiredMinPeriod;</span><br><span class="line">		</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">            <span class="comment">// if watchdog task task already exits -&gt; terminate it</span></span><br><span class="line">            <span class="keyword">if</span> (conCheckWatchDogTask != <span class="keyword">null</span>) &#123;</span><br><span class="line">                TimerTask tt = conCheckWatchDogTask;</span><br><span class="line">                conCheckWatchDogTask = <span class="keyword">null</span>;</span><br><span class="line">                tt.cancel();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// create and run new watchdog task</span></span><br><span class="line">            conCheckWatchDogTask = <span class="keyword">new</span> WachdogTask();</span><br><span class="line">            IoProvider.getTimer().schedule(conCheckWatchDogTask, watchDogPeriodConCheckMillis, watchDogPeriodConCheckMillis);</span><br><span class="line">        &#125;</span><br><span class="line">	&#125;	</span><br><span class="line">	</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WachdogTask</span> <span class="keyword">extends</span> <span class="title">TimerTask</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            check();</span><br><span class="line">        &#125;</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="function"><span class="keyword">void</span> <span class="title">check</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		watchDogRuns++;</span><br><span class="line">		<span class="keyword">long</span> current = System.currentTimeMillis();</span><br><span class="line">		ArrayList&lt;WeakReference&lt;TimeoutMgmHandle&gt;&gt; connectionsCopy = <span class="keyword">null</span>;</span><br><span class="line">		<span class="keyword">synchronized</span> (handles) &#123;</span><br><span class="line">		    connectionsCopy = (ArrayList&lt;WeakReference&lt;TimeoutMgmHandle&gt;&gt;) handles.clone();</span><br><span class="line">		&#125;</span><br><span class="line">    		</span><br><span class="line">		<span class="keyword">for</span> (WeakReference&lt;TimeoutMgmHandle&gt; handleRef : connectionsCopy) &#123;</span><br><span class="line">            	</span><br><span class="line">		    TimeoutMgmHandle handle = handleRef.get();</span><br><span class="line">		    <span class="keyword">if</span> (handle == <span class="keyword">null</span>) &#123;</span><br><span class="line">		        remove(handleRef);</span><br><span class="line">		    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		        NonBlockingConnection con = handle.getConnection();</span><br><span class="line">		        <span class="keyword">if</span> (con.isOpen()) &#123;</span><br><span class="line">		            checkTimeout(con, current);	</span><br><span class="line">		        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		            remove(handleRef);</span><br><span class="line">		        &#125;</span><br><span class="line">		    &#125;</span><br><span class="line">		&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">checkTimeout</span><span class="params">(NonBlockingConnection connection, <span class="keyword">long</span> current)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">boolean</span> timeoutOccured = connection.checkIdleTimeout(current);</span><br><span class="line">       <span class="keyword">if</span> (timeoutOccured) &#123;</span><br><span class="line">           countIdleTimeouts++;</span><br><span class="line">       &#125;</span><br><span class="line"></span><br><span class="line">       timeoutOccured = connection.checkConnectionTimeout(current);</span><br><span class="line">       <span class="keyword">if</span> (timeoutOccured) &#123;</span><br><span class="line">           countConnectionTimeouts++;</span><br><span class="line">       &#125;</span><br><span class="line">	&#125;</span><br><span class="line">	</span><br><span class="line">	<span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">TimeoutMgmHandle</span> </span>&#123;</span><br><span class="line">		</span><br><span class="line">		<span class="keyword">private</span> <span class="keyword">final</span> NonBlockingConnection con;</span><br><span class="line">		<span class="keyword">private</span> WeakReference&lt;TimeoutMgmHandle&gt; handleRef;</span><br><span class="line">		</span><br><span class="line">		<span class="function"><span class="keyword">public</span> <span class="title">TimeoutMgmHandle</span><span class="params">(NonBlockingConnection connection)</span> </span>&#123;</span><br><span class="line">			con = connection;</span><br><span class="line">			handleRef = <span class="keyword">new</span> WeakReference&lt;TimeoutMgmHandle&gt;(<span class="keyword">this</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		</span><br><span class="line">		<span class="function">WeakReference&lt;TimeoutMgmHandle&gt; <span class="title">getWeakRef</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		    <span class="keyword">return</span> handleRef;</span><br><span class="line">		&#125;</span><br><span class="line">		</span><br><span class="line">		<span class="function"><span class="keyword">void</span> <span class="title">updateCheckPeriod</span><span class="params">(<span class="keyword">long</span> period)</span> </span>&#123;</span><br><span class="line">			updateTimeoutCheckPeriod(period);</span><br><span class="line">		&#125;</span><br><span class="line">		</span><br><span class="line">		<span class="function"><span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		    <span class="keyword">if</span> (handleRef != <span class="keyword">null</span>) &#123;</span><br><span class="line">		        remove(handleRef);</span><br><span class="line">		        handleRef = <span class="keyword">null</span>;</span><br><span class="line">		    &#125;</span><br><span class="line">		&#125;</span><br><span class="line">	</span><br><span class="line">		<span class="function">NonBlockingConnection <span class="title">getConnection</span><span class="params">()</span> </span>&#123;</span><br><span class="line">			<span class="keyword">return</span> con;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　在其构造方法中，先调用方法 updateTimeoutCheckPeriod ，在该方法中新建了一个 WachdogTask 的周期性定时任务，默认60秒执行一次。每次执行的时候，都会检查每个 NonBlockingConnection 是否超时，如果超时，则处理。<br>　　注意，我们可以设置每个NonBlockingConnection的空闲时间，默认超时时间最为 Long.MAX_VALUE 。每次有新的客户端连接时，服务端都会创建一个NonBlockingConnection对象，并调用其setIdleTimeoutMillis方法设置空闲时间，并会更新WachdogTask的（全局的）周期性时间<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setIdleTimeoutMillis</span><span class="params">(<span class="keyword">long</span> timeoutMillis)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">this</span>.idleTimeoutMillis = timeoutMillis;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> ((timeoutMillis != IConnection.MAX_TIMEOUT_MILLIS) &amp;&amp; (isConnected.get())) &#123;</span><br><span class="line">		<span class="keyword">long</span> period = idleTimeoutMillis;</span><br><span class="line">		<span class="keyword">if</span> (idleTimeoutMillis &gt; <span class="number">500</span>) &#123;</span><br><span class="line">			period = idleTimeoutMillis / <span class="number">5</span>;</span><br><span class="line">		&#125;</span><br><span class="line">		timeoutMgmHandle.updateCheckPeriod(period);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　说明一下connectionTimeout和idleTimeout从开始计时时间：connectionTimeout是从建立连接开始计时；idleTimeout是从建立连接或最后一次触发onData事件时开始计时；</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/02/nio/10.xSocket系列三之源码分析二/" data-id="cijzzpv80006e1cfsj0tehgyv" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/xSocket/">xSocket</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/9.xSocket系列二之源码分析一" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/02/nio/9.xSocket系列二之源码分析一/" class="article-date">
  <time datetime="2016-01-02T01:00:00.000Z" itemprop="datePublished">2016-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/02/nio/9.xSocket系列二之源码分析一/">xSocket系列二之源码分析一</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　在上一篇文章中，我们采用xSocket开发了一个简单的示例，实现了客户端和服务端通过tcp进行通信的例子，下面我们将根据该例子讲解其中涉及到的几个重要类，对xSocket源码进行简单分析：<br>　　1. Server： 服务器端初始化线程池创建IoAcceptor<br>　　2. IoAcceptor：采用while循环接收客户端连接,并创建IoSocketDispatcher和IoChainableHandler<br>　　3. IoSocketDispatcher：负责注册SelectionKey以及事件的分发，并交给IoChainableHandler处理，通过一个while循环来处理注册的SelectionKey事件。因为不同平台的Selector实现差异比较大，通常会使用多个（大部分服务器用了两个），用一个Dispatcherpool维护，主要负责检测SocketChannel的注册、以及读写事件。需要注意Selector的key set不是线程安全的，不同线程中更新、读取需要同步。<br>　　4. IHandler：事件处理，数据的读写等等。<br>　　5. INonBlockingConnection客户端接口。</p>
<p>　　这里我们先直接列出服务端监听客户端连接并监听数据的整个过程：<br>　　1. IoAcceptor负责监听客户端的连接，为每个连接新建一个对应的IoSocketHandler，再将它注册到IoSocketDispatcher（内部注册到Selector中）<br>　　2. IoSocketDispatcher负责监听客户端数据的发送，交由 IoSocketHandlder 执行数据的读取操作。<br>　　3. IoSocketHandlder将读取到的数据通过回调，执行NonBlockingConnection的onData和onPostData方法，其中onData将数据交给ReadQueue保存。而onPostData方法则会调用HadlerAdapter的onData方法，它新建一个Runnable放入SerializationTaskQueue中内部的一个链表，并启动一个while循环，获取链表中的Runnable交给WorkQueue执行，最后执行我们定义的IHandler类。<br>　　服务端发送数据时，则将每次发送的数据写入WriteQueue中，并实时从写队列中获取一个数据交由IWriteTask写入socketchannel中</p>
<h2 id="Server"><a href="#Server" class="headerlink" title="Server"></a>Server</h2><p>　　首先我们看看Server类，它实现了接口IServer，而IServer又继承了接口java.lang.Runnable，所以可以理解它为线程。首先我们看看Server的源码，并重点关注其构造方法和start方法的实现。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Server</span> <span class="keyword">implements</span> <span class="title">IServer</span> </span>&#123;</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SIZE_WORKER_POOL = Integer.parseInt(System.getProperty(<span class="string">"org.xsocket.connection.server.workerpoolSize"</span>, <span class="string">"100"</span>));</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MIN_SIZE_WORKER_POOL = Integer.parseInt(System.getProperty(<span class="string">"org.xsocket.connection.server.workerpoolMinSize"</span>, <span class="string">"2"</span>));</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TASK_QUEUE_SIZE = Integer.parseInt(System.getProperty(<span class="string">"org.xsocket.connection.server.taskqueuesize"</span>, Integer.toString(SIZE_WORKER_POOL)));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> WAITTIME_MAXCONNECTION_EXCEEDED = Integer.parseInt(System.getProperty(<span class="string">"org.xsocket.connection.server.waittimeMaxConnectionExceeded"</span>, Integer.toString(<span class="number">500</span>)));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// acceptor</span></span><br><span class="line">    <span class="keyword">private</span> IoAcceptor acceptor;</span><br><span class="line">	</span><br><span class="line">    <span class="comment">// workerpool</span></span><br><span class="line">    <span class="keyword">private</span> ExecutorService defaultWorkerPool;</span><br><span class="line">    <span class="keyword">private</span> Executor workerpool;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//connection manager</span></span><br><span class="line">    <span class="keyword">private</span> ConnectionManager connectionManager = <span class="keyword">new</span> ConnectionManager();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> maxConcurrentConnections = Integer.MAX_VALUE;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> isMaxConnectionCheckAvtive = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// app handler</span></span><br><span class="line">    <span class="keyword">private</span> HandlerAdapter handlerAdapter = HandlerAdapter.newInstance(<span class="keyword">null</span>);</span><br><span class="line">	</span><br><span class="line">    <span class="comment">// timeouts</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">long</span> idleTimeoutMillis = IConnection.MAX_TIMEOUT_MILLIS;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">long</span> connectionTimeoutMillis = IConnection.MAX_TIMEOUT_MILLIS;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * constructor </span><br><span class="line">     * </span><br><span class="line">     * <span class="doctag">@param</span> address              local address</span><br><span class="line">     * <span class="doctag">@param</span> options              the socket options</span><br><span class="line">     * <span class="doctag">@param</span> handler              the handler to use (supported: IConnectHandler, IDisconnectHandler, IDataHandler, IIdleTimeoutHandler, IConnectionTimeoutHandler, IConnectionScoped, ILifeCycle)</span><br><span class="line">     * <span class="doctag">@param</span> sslOn                true, is SSL should be activated</span><br><span class="line">     * <span class="doctag">@param</span> sslContext           the ssl context to use</span><br><span class="line">     * <span class="doctag">@param</span> backlog              The maximum number number of pending connections. If has the value 0, or a negative value, then an implementation specific default is used.</span><br><span class="line">     * <span class="doctag">@param</span> minPoolsize          The min workerpool size</span><br><span class="line">     * <span class="doctag">@param</span> maxPoolsize          The max workerpool size</span><br><span class="line">     * <span class="doctag">@param</span> taskqueueSize        The taskqueue size</span><br><span class="line">     * <span class="doctag">@throws</span> UnknownHostException if the local host cannot determined</span><br><span class="line">     * <span class="doctag">@throws</span> IOException If some other I/O error occurs</span><br><span class="line">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="title">Server</span><span class="params">(InetSocketAddress address, Map&lt;String, Object&gt; options, IHandler handler, SSLContext sslContext, <span class="keyword">boolean</span> sslOn, <span class="keyword">int</span> backlog, <span class="keyword">int</span> minPoolsize, <span class="keyword">int</span> maxPoolsize, <span class="keyword">int</span> taskqueueSize)</span> <span class="keyword">throws</span> UnknownHostException, IOException </span>&#123;</span><br><span class="line">        defaultWorkerPool = <span class="keyword">new</span> WorkerPool(minPoolsize, maxPoolsize, taskqueueSize);</span><br><span class="line">        workerpool = defaultWorkerPool;</span><br><span class="line">        <span class="keyword">if</span> (sslContext != <span class="keyword">null</span>) &#123;</span><br><span class="line">            acceptor = ConnectionUtils.getIoProvider().createAcceptor(<span class="keyword">new</span> LifeCycleHandler(), address, backlog, options, sslContext, sslOn);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            acceptor = ConnectionUtils.getIoProvider().createAcceptor(<span class="keyword">new</span> LifeCycleHandler(), address, backlog, options);</span><br><span class="line">        &#125;</span><br><span class="line">		</span><br><span class="line">        setHandler(handler);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setHandler</span><span class="params">(IHandler handler)</span> </span>&#123;</span><br><span class="line">        handlerAdapter = HandlerAdapter.newInstance(handler);</span><br><span class="line">        initCurrentHandler();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">initCurrentHandler</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        ConnectionUtils.injectServerField(<span class="keyword">this</span>, handlerAdapter.getHandler());</span><br><span class="line">        handlerAdapter.onInit();</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">callCurrentHandlerOnDestroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        handlerAdapter.onDestroy();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        startUpTime = System.currentTimeMillis();</span><br><span class="line">        acceptor.listen();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        ConnectionUtils.start(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//资源销毁</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//如果我们定义了WorkerPool，则会启动WorkerpoolCloser服务负责销毁WorkerPool</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WorkerpoolCloser</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">        <span class="comment">//......</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　可以看到，在其构造方法中，我们初始化了WorkerPool、IoAcceptor和IHandler等关键类，后面我们再详细介绍。在其start方法中，我们调用了org.xsocket.connection.ConnectionUtils 的 start 方法新起了一个线程，该线程的run实现方法为Server的run方法。Server的run方法调用了IoAcceptor的listen方法。</p>
<h2 id="WorkerPool"><a href="#WorkerPool" class="headerlink" title="WorkerPool"></a>WorkerPool</h2><p>　　首先我们看看WorkerPool线程池的核心代码<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WorkerPool</span> <span class="keyword">extends</span> <span class="title">ThreadPoolExecutor</span> </span>&#123;</span><br><span class="line">    <span class="comment">//其他的构造方法</span></span><br><span class="line">	</span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * constructor </span><br><span class="line">     * </span><br><span class="line">     * <span class="doctag">@param</span> minSize       min worker size</span><br><span class="line">     * <span class="doctag">@param</span> maxSize       max worker size</span><br><span class="line">     * <span class="doctag">@param</span> keepalive     the keepalive</span><br><span class="line">     * <span class="doctag">@param</span> timeunit      the timeunit</span><br><span class="line">     * <span class="doctag">@param</span> taskqueuesize the task queue size </span><br><span class="line">     * <span class="doctag">@param</span> isDaemon  true, if worker threads are daemon threads</span><br><span class="line">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">WorkerPool</span><span class="params">(<span class="keyword">int</span> minSize, <span class="keyword">int</span> maxSize, <span class="keyword">long</span> keepalive, TimeUnit timeunit, <span class="keyword">int</span> taskqueuesize, <span class="keyword">boolean</span> isDaemon)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(minSize, maxSize, keepalive, timeunit, <span class="keyword">new</span> WorkerPoolAwareQueue(taskqueuesize), <span class="keyword">new</span> DefaultThreadFactory(isDaemon));</span><br><span class="line">        <span class="comment">//调用父类的getQueue方法，强转为WorkerPoolAwareQueue</span></span><br><span class="line">        ((WorkerPoolAwareQueue) getQueue()).init(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">WorkerPoolAwareQueue</span> <span class="keyword">extends</span> <span class="title">LinkedBlockingQueue</span>&lt;<span class="title">Runnable</span>&gt; </span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">private</span> WorkerPool workerPool;</span><br><span class="line">        </span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">WorkerPoolAwareQueue</span><span class="params">(<span class="keyword">int</span> capacity)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">super</span>(capacity);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(WorkerPool workerPool)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.workerPool = workerPool;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//向queue中插入数据</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">offer</span><span class="params">(Runnable task)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 线程池中正在活动（执行）的线程数比池中的线程数要小</span></span><br><span class="line">            <span class="keyword">if</span> (workerPool.getActiveCount() &lt; workerPool.getPoolSize()) &#123;</span><br><span class="line">				<span class="comment">//有空闲的线程可用，立即执行</span></span><br><span class="line">                <span class="comment">// </span></span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">super</span>.offer(task);</span><br><span class="line">            &#125;</span><br><span class="line">			<span class="comment">//上一步的判断后，运行到此处则说明：</span></span><br><span class="line">            <span class="comment">//此时线程池中的线程（全部为活动的）数超过了线程池的最大值</span></span><br><span class="line">            <span class="keyword">if</span> (workerPool.getPoolSize() &gt;= workerPool.getMaximumPoolSize()) &#123;</span><br><span class="line">                <span class="comment">// 将task添加到queue尾部，等待一个可用的线程处理该task</span></span><br><span class="line">                <span class="keyword">if</span> (LOG.isLoggable(Level.FINE)) &#123;</span><br><span class="line">                    LOG.fine(<span class="string">"add task to queue waiting for the next free one"</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">super</span>.offer(task);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// return false, which forces starting a new thread. The new</span></span><br><span class="line">                <span class="comment">// thread performs the task and will be added to workerpool</span></span><br><span class="line">                <span class="comment">// after performing the task </span></span><br><span class="line">                <span class="comment">// As result the workerpool is increased. If the thread reach</span></span><br><span class="line">                <span class="comment">// its keepalive timeout (waiting for new tasks), the thread </span></span><br><span class="line">                <span class="comment">// will terminate itself</span></span><br><span class="line">                </span><br><span class="line">                <span class="keyword">if</span> (LOG.isLoggable(Level.FINE)) &#123;</span><br><span class="line">                    LOG.fine(<span class="string">"initiate creating a new thread"</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">DefaultThreadFactory</span> <span class="keyword">implements</span> <span class="title">ThreadFactory</span> </span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> AtomicInteger poolNumber = <span class="keyword">new</span> AtomicInteger(<span class="number">1</span>);</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> AtomicInteger threadNumber = <span class="keyword">new</span> AtomicInteger(<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> String namePrefix;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">boolean</span> isDaemon;</span><br><span class="line"></span><br><span class="line">        DefaultThreadFactory(<span class="keyword">boolean</span> isDaemon) &#123;</span><br><span class="line">            <span class="keyword">this</span>.isDaemon = isDaemon;</span><br><span class="line">            namePrefix = <span class="string">"xWorkerPool-"</span> + poolNumber.getAndIncrement() + <span class="string">"-thread-"</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> Thread <span class="title">newThread</span><span class="params">(Runnable r)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (LOG.isLoggable(Level.FINE)) &#123;</span><br><span class="line">                LOG.fine(<span class="string">"creating new thread"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            Thread t = <span class="keyword">new</span> Thread(r, namePrefix + threadNumber.getAndIncrement());</span><br><span class="line">            t.setDaemon(isDaemon);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (t.getPriority() != Thread.NORM_PRIORITY) &#123;</span><br><span class="line">                t.setPriority(Thread.NORM_PRIORITY);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> t;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>下面我们看看IoAcceptor的源码，并重点关注其 listen 方法。</p>
<h2 id="IoAcceptor"><a href="#IoAcceptor" class="headerlink" title="IoAcceptor"></a>IoAcceptor</h2><p>　　在xSocket中，IoAcceptor主要负责处理客户端的连接，下面为其关键代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">IoAcceptor</span>  </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> IIoAcceptorCallback callback;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ServerSocketChannel serverChannel;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> IoSocketDispatcherPool dispatcherPool;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">IoAcceptor</span><span class="params">(IIoAcceptorCallback callback, InetSocketAddress address, <span class="keyword">int</span> backlog, SSLContext sslContext, <span class="keyword">boolean</span> sslOn, <span class="keyword">boolean</span> isReuseAddress)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.callback = callback;</span><br><span class="line"></span><br><span class="line">        serverChannel = ServerSocketChannel.open();</span><br><span class="line">        <span class="keyword">assert</span> (serverChannel != <span class="keyword">null</span>);</span><br><span class="line">        </span><br><span class="line">        serverChannel.configureBlocking(<span class="keyword">true</span>);</span><br><span class="line">        serverChannel.socket().setSoTimeout(<span class="number">0</span>);  <span class="comment">// accept method never times out</span></span><br><span class="line">        serverChannel.socket().setReuseAddress(isReuseAddress); </span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            serverChannel.socket().bind(address, backlog);</span><br><span class="line">            dispatcherPool = <span class="keyword">new</span> IoSocketDispatcherPool(<span class="string">"Srv"</span> + getLocalPort(), IoProvider.getServerDispatcherInitialSize());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (BindException be) &#123;</span><br><span class="line">        	serverChannel.close();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">listen</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    	callback.onConnected();</span><br><span class="line">    	accept();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">accept</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (isOpen.get()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// blocking accept call</span></span><br><span class="line">                SocketChannel channel = serverChannel.accept();</span><br><span class="line"></span><br><span class="line">                <span class="comment">//新的客户端连接，获取一个IoSocketDispatcher</span></span><br><span class="line">                IoSocketDispatcher dispatcher = dispatcherPool.nextDispatcher();</span><br><span class="line">                <span class="comment">//同时创建一个链式处理器</span></span><br><span class="line">                IoChainableHandler ioHandler = ConnectionUtils.getIoProvider().createIoHandler(<span class="keyword">false</span>, dispatcher, channel, sslContext, sslOn);</span><br><span class="line">                <span class="comment">//callback对象为Server的内部类LifeCycleHandler</span></span><br><span class="line">                callback.onConnectionAccepted(ioHandler);</span><br><span class="line">				<span class="comment">//连接数+1</span></span><br><span class="line">                acceptedConnections++;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　在IoAcceptor的构造方法中，我们打开了ServerSocketChannel，并新建了一个IoSocketDispatcherPool对象。accept方法则主要负责处理客户端的连接，每当有新的连接建立时，就会从IoSocketDispatcherPool中获取一个IoSocketDispatcher并生成一个IoChainableHandler，IoChainableHandler包含了SocketChannel、IoSocketDispatcher等信息。最后执行callback的onConnectionAccepted方法，这里的callback实际上为LifeCycleHandler类的一个实例。</p>
<h2 id="IoSocketDispatcher"><a href="#IoSocketDispatcher" class="headerlink" title="IoSocketDispatcher"></a>IoSocketDispatcher</h2><p>　　在介绍IoSocketDispatcher之前，我们先简单说一下IoSocketDispatcherPool。顾名思义，IoSocketDispatcherPool就是一个容纳IoSocketDispatcher的池。在IOAcceptor初始化时，同时初始化该池的大小为2，并初始化相应数量（这里为2）的IoSocketDispatcher。注意，每次初始化IoSocketDispatcher（Runnable类型）后，都会启动它，我们看看其初始化启动的关键代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">updateDispatcher</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; size; i++) &#123;</span><br><span class="line">        IoUnsynchronizedMemoryManager memoryManager = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (preallocation) &#123;</span><br><span class="line">            memoryManager = IoUnsynchronizedMemoryManager.createPreallocatedMemoryManager(preallocationSize, bufferMinsize, useDirect);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            memoryManager = IoUnsynchronizedMemoryManager.createNonPreallocatedMemoryManager(useDirect);</span><br><span class="line">        &#125;</span><br><span class="line">		</span><br><span class="line">        IoSocketDispatcher dispatcher = <span class="keyword">new</span> IoSocketDispatcher(memoryManager, name + <span class="string">"#"</span> + i);</span><br><span class="line">        dispatchers.addLast(dispatcher);</span><br><span class="line"></span><br><span class="line">        Thread t = <span class="keyword">new</span> Thread(dispatcher);</span><br><span class="line">        t.setDaemon(<span class="keyword">true</span>);</span><br><span class="line">        t.start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (IIoDispatcherPoolListener listener : listeners) &#123;</span><br><span class="line">            listener.onDispatcherAdded(dispatcher);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　每当有新的连接到来时，根据round-robin算法从上面的IoSocketDispatcherPool池中选择一个IoSocketDispatcher，用来处理该连接。下面我们看看IoSocketDispatcher的关键代码，如run、handleReadWriteKeys、onReadableEvent、onWriteableEvent和register等方法!<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">IoSocketDispatcher</span> <span class="keyword">extends</span> <span class="title">MonitoredSelector</span> <span class="keyword">implements</span> <span class="title">Runnable</span>, <span class="title">Closeable</span> </span>&#123;</span><br><span class="line">	<span class="comment">// queues</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ConcurrentLinkedQueue&lt;Runnable&gt; registerQueue = <span class="keyword">new</span> ConcurrentLinkedQueue&lt;Runnable&gt;();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ConcurrentLinkedQueue&lt;IoSocketHandler&gt; deregisterQueue = <span class="keyword">new</span> ConcurrentLinkedQueue&lt;IoSocketHandler&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Selector selector;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">IoSocketDispatcher</span><span class="params">(AbstractMemoryManager memoryManager, String name)</span>  </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.memoryManager = memoryManager;</span><br><span class="line">        <span class="comment">//省略其他代码</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            selector = Selector.open();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException ioe) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">reinit</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">//重新open一个Selector，并替换之前的</span></span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Thread.currentThread().setName(name);</span><br><span class="line">        <span class="keyword">while</span>(isOpen.get()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">int</span> eventCount = selector.select(<span class="number">5000</span>); </span><br><span class="line">                <span class="keyword">if</span> (eventCount &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    handleReadWriteKeys();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable e) &#123;&#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//循环结束，处理关闭</span></span><br><span class="line">        <span class="keyword">for</span> (IoSocketHandler socketHandler : getRegistered()) &#123;</span><br><span class="line">            socketHandler.onDeregisteredEvent();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            selector.close();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//处理读写时间</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleReadWriteKeys</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Set&lt;SelectionKey&gt; selectedEventKeys = selector.selectedKeys();</span><br><span class="line">        Iterator&lt;SelectionKey&gt; it = selectedEventKeys.iterator();</span><br><span class="line">        <span class="comment">// handle read &amp; write</span></span><br><span class="line">        <span class="keyword">while</span> (it.hasNext()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                SelectionKey eventKey = it.next();</span><br><span class="line">                it.remove();</span><br><span class="line">                </span><br><span class="line">                IoSocketHandler socketHandler = (IoSocketHandler) eventKey.attachment();</span><br><span class="line">   </span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 读事件</span></span><br><span class="line">                    <span class="keyword">if</span> (eventKey.isValid() &amp;&amp; eventKey.isReadable()) &#123;</span><br><span class="line">                        onReadableEvent(socketHandler);</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">// 写事件</span></span><br><span class="line">                    <span class="keyword">if</span> (eventKey.isValid() &amp;&amp; eventKey.isWritable()) &#123;</span><br><span class="line">                        onWriteableEvent(socketHandler);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">        &#125;</span><br><span class="line">	&#125;</span><br><span class="line">    <span class="comment">//处理读取事件</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">onReadableEvent</span><span class="params">(IoSocketHandler socketHandler)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">long</span> read = socketHandler.onReadableEvent();</span><br><span class="line">            receivedBytes += read;</span><br><span class="line">            handledReads++;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception t) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//处理io写事件</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">onWriteableEvent</span><span class="params">(IoSocketHandler socketHandler)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            socketHandler.onWriteableEvent();</span><br><span class="line">            handledWrites++;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">register</span><span class="params">(IoSocketHandler socketHandler, <span class="keyword">int</span> ops)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        socketHandler.setMemoryManager(memoryManager);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (isDispatcherInstanceThread()) &#123;</span><br><span class="line">            registerHandlerNow(socketHandler, ops);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            registerQueue.add(<span class="keyword">new</span> RegisterTask(socketHandler, ops));</span><br><span class="line">            wakeUp();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">RegisterTask</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> IoSocketHandler socketHandler;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> ops;</span><br><span class="line">				</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">RegisterTask</span><span class="params">(IoSocketHandler socketHandler, <span class="keyword">int</span> ops)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.socketHandler = socketHandler;</span><br><span class="line">            <span class="keyword">this</span>.ops = ops;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                registerHandlerNow(socketHandler, ops);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException ioe) &#123;&#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">setWriteSelectionKeyNow</span><span class="params">(<span class="keyword">final</span> IoSocketHandler socketHandler)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        SelectionKey key = getSelectionKey(socketHandler);</span><br><span class="line">        <span class="keyword">if</span> (key != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!isWriteable(key)) &#123;</span><br><span class="line">                key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">	    	<span class="keyword">throw</span> <span class="keyword">new</span> IOException(<span class="string">"["</span> + socketHandler.getId() + <span class="string">"] Error occured by setting write selection key. key is null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//取消指定连接的写兴趣</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">unsetWriteSelectionKeyNow</span><span class="params">(<span class="keyword">final</span> IoSocketHandler socketHandler)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        SelectionKey key = getSelectionKey(socketHandler);</span><br><span class="line">        <span class="keyword">if</span> (key != <span class="keyword">null</span>) &#123;</span><br><span class="line">        	<span class="keyword">if</span> (isWriteable(key)) &#123;</span><br><span class="line">                key.interestOps(key.interestOps() &amp; ~SelectionKey.OP_WRITE);   </span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IOException(<span class="string">"["</span> + socketHandler.getId() + <span class="string">"] Error occured by unsetting write selection key. key is null"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//将socketHandler对应的连接注册到Selector上</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">registerHandlerNow</span><span class="params">(IoSocketHandler socketHandler, <span class="keyword">int</span> ops)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (socketHandler.isOpen()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                socketHandler.getChannel().register(selector, ops, socketHandler);				</span><br><span class="line">                socketHandler.onRegisteredEvent();</span><br><span class="line"></span><br><span class="line">                handledRegistractions++;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            socketHandler.onRegisteredFailedEvent(<span class="keyword">new</span> IOException(<span class="string">"could not register handler "</span> + socketHandler.getId() + <span class="string">" because the channel is closed"</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//每秒数据接收量</span></span><br><span class="line">    <span class="function"><span class="keyword">long</span> <span class="title">getReceiveRateBytesPerSec</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> rate = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">long</span> elapsed = System.currentTimeMillis() - lastRequestReceiveRate;</span><br><span class="line">        <span class="keyword">if</span> (receivedBytes == <span class="number">0</span>) &#123;</span><br><span class="line">            rate = <span class="number">0</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (elapsed == <span class="number">0</span>) &#123;</span><br><span class="line">            rate = Long.MAX_VALUE;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            rate = ((receivedBytes * <span class="number">1000</span>) / elapsed);</span><br><span class="line">        &#125;</span><br><span class="line">        lastRequestReceiveRate = System.currentTimeMillis();</span><br><span class="line">        receivedBytes = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> rate;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//每秒数据发送量</span></span><br><span class="line">    <span class="function"><span class="keyword">long</span> <span class="title">getSendRateBytesPerSec</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> rate = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">long</span> elapsed = System.currentTimeMillis() - lastRequestSendRate;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (sentBytes == <span class="number">0</span>) &#123;</span><br><span class="line">            rate = <span class="number">0</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (elapsed == <span class="number">0</span>) &#123;</span><br><span class="line">            rate = Long.MAX_VALUE;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            rate = ((sentBytes * <span class="number">1000</span>) / elapsed);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        lastRequestSendRate = System.currentTimeMillis();</span><br><span class="line">        sentBytes = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> rate;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　我们可以看到，每个 IoSocketDispatcher 都会各自打开一个 Selector ，当新的连接建立后，我们选择一个 IoSocketDispatcher ，然后将该链接注册到相应的 Selector 上。在 IoSocketDispatcher 的run方法，中，会通过while循环实时获取连接的读写事件，最后交由相应的 IoSocketHandler 处理。而 IoSocketHandler 则为 IoChainableHandler 的一个子类。</p>
<h2 id="IoChainableHandler"><a href="#IoChainableHandler" class="headerlink" title="IoChainableHandler"></a>IoChainableHandler</h2><p>　　每次有新客户端连接时，都会新建一个IoChainableHandler用于该连接后续事件的处理，即一个IoChainableHandler负责一个连接的事件处理，下面我们看看IoChainableHandler是如何创建的!<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">IoChainableHandler <span class="title">createIoHandler</span><span class="params">(<span class="keyword">boolean</span> isClient, IoSocketDispatcher dispatcher, SocketChannel channel, SSLContext sslContext, <span class="keyword">boolean</span> sslOn)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    String connectionId = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (isClient) &#123;</span><br><span class="line">        connectionId = idPrefix + <span class="string">"C"</span> + Integer.toHexString(nextId.incrementAndGet());</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        connectionId = idPrefix + <span class="string">"S"</span> + Integer.toHexString(nextId.incrementAndGet());</span><br><span class="line">    &#125;</span><br><span class="line">    IoChainableHandler ioHandler = <span class="keyword">new</span> IoSocketHandler(channel, dispatcher, connectionId);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ssl connection?</span></span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="keyword">return</span> ioHandler;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　我们在 IoSocketDispatcher 里面说到了， IoSocketDispatcher 负责检测socketchannel的读写状态，最后会调用到 IoSocketHandler 的 onReadableEvent 方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">long</span> <span class="title">onReadableEvent</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    <span class="keyword">long</span> read = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// read data from socket</span></span><br><span class="line">    ByteBuffer[] received  = readSocket();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// handle the data</span></span><br><span class="line">    <span class="keyword">if</span> (received != <span class="keyword">null</span>) &#123;</span><br><span class="line">	    <span class="keyword">int</span> size = <span class="number">0</span>;</span><br><span class="line">	    <span class="keyword">for</span> (ByteBuffer byteBuffer : received) &#123;</span><br><span class="line">		    size += byteBuffer.remaining();</span><br><span class="line">	    &#125;</span><br><span class="line">	    read += size;</span><br><span class="line">        <span class="comment">// getPreviousCallback()即返回了之前NonBlockingConnection传的IoHandlerCallback</span></span><br><span class="line">        <span class="comment">// 它是NonBlockingConnection的一个内部类，它的方法直接调用了NonBlockingConnection相应的方法，</span></span><br><span class="line">		<span class="comment">// 这个内部类可以看做只是提供了闭包的作用，甚至可以将NonBlockingConnection直接传给IoSocketHandler的。</span></span><br><span class="line">	    getPreviousCallback().onData(received, size);</span><br><span class="line">	    getPreviousCallback().onPostData();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// increase preallocated read memory if not sufficient</span></span><br><span class="line">    checkPreallocatedReadMemory();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> read;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　onReadableEvent内部将读取到的数据通过 NonBlockingConnection 的 onData 方法回调，然后会将数据加入到NonBlockingConnection的readQueue中。<br>　　之后执行 NonBlockingConnection 的 onPostData ，onPostData内部继续调用 HandlerAdapter 的 onData 方法。下面我们看看 HandlerAdapter 的 onData 方法。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onData</span><span class="params">(INonBlockingConnection connection, SerializedTaskQueue taskQueue, Executor workerpool, <span class="keyword">boolean</span> ignoreException, <span class="keyword">boolean</span> isUnsynchronized)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (handlerInfo.isDataHandler()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (handlerInfo.isUnsynchronized()) &#123;</span><br><span class="line">            performOnData(connection, taskQueue, ignoreException, (IDataHandler) handler);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (getHandlerInfo().isDataHandlerMultithreaded()) &#123;</span><br><span class="line">                taskQueue.performMultiThreaded(<span class="keyword">new</span> PerformOnDataTask(connection, taskQueue, ignoreException, (IDataHandler) handler), workerpool);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (isUnsynchronized) &#123;</span><br><span class="line">                    performOnData(connection, taskQueue, ignoreException, (IDataHandler) handler);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    taskQueue.performNonThreaded(<span class="keyword">new</span> PerformOnDataTask(connection, taskQueue, ignoreException, (IDataHandler) handler), workerpool);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　内部执行了 taskQueue 的 performMultiThreaded 方法。这里简单说说SerializedTaskQueue类，它封装了一个Runnable 链表保存任务，一个执行线程执行链表中的任务。<br>　　上面的方法将新创建的 PerFormOnDataTask （HandlerAdapter内部类）加入到了链表，并使用workpool启动了执行线程，这样就执行了PerformOnData的run方法。之前的操作都是在Dispatcher线程中，到此Dispatcher线程重新回到循环中等待新的事件。也就是每次我们的逻辑代码都是用的独立的线程执行的，PerformOnDataTask的run方法调用了HandlerAdapter的performOnData函数。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">performOnData</span><span class="params">(INonBlockingConnection connection, SerializedTaskQueue taskQueue, <span class="keyword">boolean</span> ignoreException, IDataHandler handler)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">		<span class="comment">// loop until readQueue is empty or nor processing has been done (readQueue has not been modified)</span></span><br><span class="line">        <span class="keyword">while</span> ((connection.available() != <span class="number">0</span>) &amp;&amp; !connection.isReceivingSuspended()) &#123;</span><br><span class="line">            <span class="keyword">int</span> version = connection.getReadBufferVersion();</span><br><span class="line">			handler.onData(connection);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　performOnData方法最终都执行了IDataHandler的onData方法，而它就是我们之前定义的ServerHandler类！</p>
<h2 id="LifeCycleHandler"><a href="#LifeCycleHandler" class="headerlink" title="LifeCycleHandler"></a>LifeCycleHandler</h2><p>　　每次新的连接到来时，由Server的内部类LifeCycleHandler的onConnectionAccepted方法负责该链接的初始化工作，下面我们看看其源码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">LifeCycleHandler</span> <span class="keyword">implements</span> <span class="title">IIoAcceptorCallback</span> </span>&#123;</span><br><span class="line">    <span class="comment">//服务端启动时执行一次</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onConnected</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        isOpen.set(<span class="keyword">true</span>);</span><br><span class="line">        <span class="comment">// notify listeners</span></span><br><span class="line">        <span class="keyword">for</span> (IServerListener listener : (ArrayList&lt;IServerListener&gt;) listeners.clone()) &#123;</span><br><span class="line">            listener.onInit();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDisconnected</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// perform handler callback </span></span><br><span class="line">        callCurrentHandlerOnDestroy();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// calling server listener </span></span><br><span class="line">        <span class="keyword">for</span> (IServerListener listener : (ArrayList&lt;IServerListener&gt;)listeners.clone()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                listener.onDestroy();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException ioe) &#123;&#125;</span><br><span class="line">		&#125;</span><br><span class="line">        listeners.clear();</span><br><span class="line">		</span><br><span class="line">        <span class="comment">// close default worker pool if exists</span></span><br><span class="line">        <span class="keyword">if</span> (defaultWorkerPool != <span class="keyword">null</span>) &#123;</span><br><span class="line">            WorkerpoolCloser workerpoolCloser = <span class="keyword">new</span> WorkerpoolCloser(Server.<span class="keyword">this</span>);</span><br><span class="line">            workerpoolCloser.start();</span><br><span class="line">        &#125;</span><br><span class="line">		</span><br><span class="line">        <span class="comment">// unset workerpool reference </span></span><br><span class="line">        workerpool = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">	<span class="comment">//当新的客户端与服务端建立连接时，执行</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onConnectionAccepted</span><span class="params">(IoChainableHandler ioHandler)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">// 如果达到了服务端的最大连接数，则禁止连接</span></span><br><span class="line">        <span class="keyword">if</span> (isMacConnectionSizeExceeded()) &#123;</span><br><span class="line">            <span class="comment">// create connection</span></span><br><span class="line">            NonBlockingConnection connection = <span class="keyword">new</span> NonBlockingConnection(connectionManager, HandlerAdapter.newInstance(<span class="keyword">null</span>));</span><br><span class="line">            init(connection, ioHandler);</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                onPreRejectConnection(connection);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;&#125;</span><br><span class="line">			</span><br><span class="line">            <span class="comment">// ... and than reject it</span></span><br><span class="line">            connection.closeQuietly();</span><br><span class="line">            <span class="comment">// wait while max connection size is exceeded</span></span><br><span class="line">            do &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep(WAITTIME_MAXCONNECTION_EXCEEDED);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException ie) &#123;&#125;</span><br><span class="line">            &#125; <span class="keyword">while</span> (isMacConnectionSizeExceeded());</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 正常处理客户端的连接事件</span></span><br><span class="line">            NonBlockingConnection connection = <span class="keyword">new</span> NonBlockingConnection(connectionManager, handlerAdapter.getConnectionInstance());</span><br><span class="line">            init(connection, ioHandler);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//判断是否达到了服务端的最大连接数</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">isMacConnectionSizeExceeded</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (isMaxConnectionCheckAvtive &amp;&amp; </span><br><span class="line">                isOpen.get() &amp;&amp; </span><br><span class="line">                (acceptor.getDispatcherPool().getRoughNumRegisteredHandles() &gt;= maxConcurrentConnections) &amp;&amp;</span><br><span class="line">                (acceptor.getDispatcherPool().getNumRegisteredHandles() &gt;= maxConcurrentConnections));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//对连接进行必要的初始化设置</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(NonBlockingConnection connection, IoChainableHandler ioHandler)</span> <span class="keyword">throws</span> SocketTimeoutException, IOException </span>&#123;</span><br><span class="line">        <span class="comment">// set default flush properties</span></span><br><span class="line">        connection.setAutoflush(autoflush);</span><br><span class="line">        connection.setFlushmode(flushMode);</span><br><span class="line">        connection.setWorkerpool(workerpool);</span><br><span class="line">        </span><br><span class="line">        connection.init(ioHandler);</span><br><span class="line">			</span><br><span class="line">        <span class="comment">// 设置长连接闲置超时</span></span><br><span class="line">        connection.setIdleTimeoutMillis(idleTimeoutMillis);</span><br><span class="line">        <span class="comment">//设置连接超时</span></span><br><span class="line">        connection.setConnectionTimeoutMillis(connectionTimeoutMillis);</span><br><span class="line">        <span class="comment">// 限制输出流量</span></span><br><span class="line">        <span class="keyword">if</span> (writeRate != <span class="keyword">null</span>) &#123;</span><br><span class="line">             connection.setWriteTransferRate(writeRate);</span><br><span class="line">        &#125;</span><br><span class="line">		</span><br><span class="line">        <span class="comment">// and threshold</span></span><br><span class="line">        <span class="keyword">if</span> (maxReadBufferThreshold != <span class="keyword">null</span>) &#123;</span><br><span class="line">            connection.setMaxReadBufferThreshold(maxReadBufferThreshold);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　每次有新的客户端连接后，都会执行onConnectionAccepted方法。该方法首先判断是否达到了服务端的最大连接数，如果没有，则进行正常的连接处理，包括连接参数初始化等。接着设置NonBlockingConnection和IoChainableHandler初始化参数。</p>
<h2 id="NonBlockingConnection"><a href="#NonBlockingConnection" class="headerlink" title="NonBlockingConnection"></a>NonBlockingConnection</h2><p>　　在客户端的代码中，我们新建了一个INonBlockingConnection对象，下面我们看看其关键的几行代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="title">NonBlockingConnection</span><span class="params">(InetSocketAddress remoteAddress, InetSocketAddress localAddress, <span class="keyword">boolean</span> waitForConnect, <span class="keyword">int</span> connectTimeoutMillis, <span class="keyword">final</span> Map&lt;String, Object&gt; options, <span class="keyword">final</span> SSLContext sslContext, <span class="keyword">boolean</span> isSecured,  IHandler appHdl, Executor workerpool, <span class="keyword">boolean</span> autoflush, FlushMode flushmode, Object attachment)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    setFlushmode(flushmode);</span><br><span class="line">    setAutoflush(autoflush);</span><br><span class="line">    setWorkerpool(workerpool);</span><br><span class="line">	</span><br><span class="line">    setAttachment(attachment);</span><br><span class="line">	</span><br><span class="line">    handlerAdapterRef.set(HandlerAdapter.newInstance(appHdl));</span><br><span class="line">    isServerSide = <span class="keyword">false</span>;</span><br><span class="line">	</span><br><span class="line">    SocketChannel channel = openSocket(localAddress, options);</span><br><span class="line">	</span><br><span class="line">    IoConnector connector = getDefaultConnector();</span><br><span class="line">	</span><br><span class="line">	<span class="comment">// sync connect</span></span><br><span class="line">    <span class="keyword">if</span> (waitForConnect &amp;&amp; (connectTimeoutMillis &gt; <span class="number">0</span>)) &#123;</span><br><span class="line">        SyncIoConnectorCallback callback = <span class="keyword">new</span> SyncIoConnectorCallback(remoteAddress, channel, sslContext, isSecured, connectTimeoutMillis);</span><br><span class="line">        connector.connectAsync(channel, remoteAddress, connectTimeoutMillis, callback);</span><br><span class="line">        callback.connect();</span><br><span class="line">	<span class="comment">// async connect			</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        IIoConnectorCallback callback = <span class="keyword">new</span> AsyncIoConnectorCallback(remoteAddress, channel, sslContext, isSecured, connectTimeoutMillis);</span><br><span class="line">        connector.connectAsync(channel, remoteAddress, connectTimeoutMillis, callback);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> SocketChannel <span class="title">openSocket</span><span class="params">(InetSocketAddress localAddress, Map&lt;String ,Object&gt; options)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">    SocketChannel channel = SocketChannel.open();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (Entry&lt;String, Object&gt; entry : options.entrySet()) &#123;</span><br><span class="line">        IoProvider.setOption(channel.socket(), entry.getKey(), entry.getValue());</span><br><span class="line">    &#125;</span><br><span class="line">        </span><br><span class="line">    <span class="keyword">if</span> (localAddress != <span class="keyword">null</span>) &#123;</span><br><span class="line">        channel.socket().bind(localAddress);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> channel;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">synchronized</span> <span class="keyword">static</span> IoConnector <span class="title">getDefaultConnector</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (defaultConnector == <span class="keyword">null</span>) &#123;</span><br><span class="line">        defaultConnector = <span class="keyword">new</span> IoConnector(<span class="string">"default"</span>);</span><br><span class="line"></span><br><span class="line">        Thread t = <span class="keyword">new</span> Thread(defaultConnector);</span><br><span class="line">        t.setDaemon(<span class="keyword">true</span>);</span><br><span class="line">        t.start();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> defaultConnector;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　在构造方法中建立连接，并生成IoConnector用来管理连接，然后connector开始启动，做一些初始化的工作：其中connectAsync方法会执行会产生一个RegisterTask任务到IoConnector。下面我们终端关注 IoConnector 的run、connectAsync等方法<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">IoConnector</span> <span class="keyword">extends</span> <span class="title">MonitoredSelector</span> <span class="keyword">implements</span> <span class="title">Runnable</span>, <span class="title">Closeable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> TimeoutCheckTask timeoutCheckTask = <span class="keyword">new</span> TimeoutCheckTask();</span><br><span class="line">    <span class="keyword">private</span> TimerTask watchDogTask;</span><br><span class="line">    <span class="comment">// selector </span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Selector selector;</span><br><span class="line">    <span class="comment">// queues</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ConcurrentLinkedQueue&lt;Runnable&gt; taskQueue = <span class="keyword">new</span> ConcurrentLinkedQueue&lt;Runnable&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">IoConnector</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            selector = Selector.open();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException ioe) &#123;&#125;     </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">int</span> handledTasks = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(isOpen.get()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                handledTasks = performTaskQueue();</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">int</span> eventCount = selector.select(<span class="number">1000</span>);</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">if</span> (eventCount &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    handleConnect();</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                	checkForLooping(handledTasks);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            selector.close();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">performTaskQueue</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> handledTasks = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            Runnable task = taskQueue.poll();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (task == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> handledTasks;</span><br><span class="line"></span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                task.run();</span><br><span class="line">                handledTasks++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleConnect</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Set&lt;SelectionKey&gt; selectedEventKeys = selector.selectedKeys();</span><br><span class="line"></span><br><span class="line">        Iterator&lt;SelectionKey&gt; it = selectedEventKeys.iterator();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span> (it.hasNext()) &#123;</span><br><span class="line">            SelectionKey eventKey = it.next();</span><br><span class="line">            it.remove();</span><br><span class="line"></span><br><span class="line">            RegisterTask registerTask = (RegisterTask) eventKey.attachment();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (eventKey.isValid() &amp;&amp; eventKey.isConnectable()) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">boolean</span> isConnected = ((SocketChannel) eventKey.channel()).finishConnect();</span><br><span class="line">                    <span class="keyword">if</span> (isConnected) &#123;</span><br><span class="line">						eventKey.cancel();</span><br><span class="line">                    	registerTask.callback.onConnectionEstablished();</span><br><span class="line">					&#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException ioe) &#123;&#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">      </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">connectAsync</span><span class="params">(SocketChannel channel, InetSocketAddress remoteAddress, <span class="keyword">long</span> connectTimeoutMillis, IIoConnectorCallback callback)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        RegisterTask registerTask = <span class="keyword">new</span> RegisterTask(channel, callback, remoteAddress, System.currentTimeMillis() + (<span class="keyword">long</span>) connectTimeoutMillis);</span><br><span class="line">        addToTaskQueue(registerTask);</span><br><span class="line">                </span><br><span class="line">        <span class="keyword">if</span> (connectTimeoutMillis &gt;= <span class="number">1000</span>) &#123;</span><br><span class="line">            updateTimeoutCheckPeriod(connectTimeoutMillis / <span class="number">5</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            updateTimeoutCheckPeriod(<span class="number">200</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">addToTaskQueue</span><span class="params">(Runnable task)</span> </span>&#123;</span><br><span class="line">        taskQueue.add(task);</span><br><span class="line">        selector.wakeup();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">RegisterTask</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> SocketChannel channel;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> IIoConnectorCallback callback;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> InetSocketAddress remoteAddress;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">long</span> expireTime;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">private</span> SelectionKey selectionKey;</span><br><span class="line">        </span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">RegisterTask</span><span class="params">(SocketChannel channel, IIoConnectorCallback callback, InetSocketAddress remoteAddress, <span class="keyword">long</span> expireTime)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.channel = channel;</span><br><span class="line">            <span class="keyword">this</span>.callback = callback;</span><br><span class="line">            <span class="keyword">this</span>.remoteAddress = remoteAddress;</span><br><span class="line">            <span class="keyword">this</span>.expireTime = expireTime;</span><br><span class="line">            </span><br><span class="line">            channel.configureBlocking(<span class="keyword">false</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="function"><span class="keyword">boolean</span> <span class="title">isExpired</span><span class="params">(<span class="keyword">long</span> currentTime)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> (currentTime &gt; expireTime); </span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">          </span><br><span class="line">            selectionKey = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                selectionKey = channel.register(selector, SelectionKey.OP_CONNECT);</span><br><span class="line">                selectionKey.attach(<span class="keyword">this</span>);</span><br><span class="line">                connect(channel, remoteAddress);</span><br><span class="line">            </span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException ioe) &#123;&#125;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">connect</span><span class="params">(SocketChannel channel, InetSocketAddress remoteAddress)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                channel.connect(remoteAddress);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (UnresolvedAddressException uae) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IOException(<span class="string">"connecting "</span> + remoteAddress + <span class="string">" failed "</span> + uae.toString());</span><br><span class="line">            &#125; </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">updateTimeoutCheckPeriod</span><span class="params">(<span class="keyword">long</span> requiredMinPeriod)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// if non watchdog task already exists and the required period is smaller than current one -&gt; return</span></span><br><span class="line">            <span class="keyword">if</span> ((watchDogTask != <span class="keyword">null</span>) &amp;&amp; (watchDogPeriodMillis &lt;= requiredMinPeriod)) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">    </span><br><span class="line">            <span class="comment">// set watch dog period</span></span><br><span class="line">            watchDogPeriodMillis = requiredMinPeriod;</span><br><span class="line">            <span class="keyword">if</span> (LOG.isLoggable(Level.FINE)) &#123;</span><br><span class="line">                LOG.fine(<span class="string">"update watchdog period "</span> + DataConverter.toFormatedDuration(watchDogPeriodMillis));</span><br><span class="line">            &#125;</span><br><span class="line">    </span><br><span class="line">            <span class="comment">// if watchdog task task already exits -&gt; terminate it</span></span><br><span class="line">            <span class="keyword">if</span> (watchDogTask != <span class="keyword">null</span>) &#123;</span><br><span class="line">                watchDogTask.cancel();</span><br><span class="line">                watchDogTask = <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// create and run new watchdog task</span></span><br><span class="line">            watchDogTask = <span class="keyword">new</span> TimerTask() &#123;</span><br><span class="line">        </span><br><span class="line">                <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                    addToTaskQueue(timeoutCheckTask);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;;</span><br><span class="line">            IoProvider.getTimer().schedule(watchDogTask, watchDogPeriodMillis, watchDogPeriodMillis);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;   </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">TimeoutCheckTask</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">        </span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">long</span> currentMillis = System.currentTimeMillis();</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">for</span> (SelectionKey selectionKey : selector.keys()) &#123;</span><br><span class="line">                    RegisterTask registerTask = (RegisterTask) selectionKey.attachment();</span><br><span class="line">                    <span class="keyword">if</span> (registerTask.isExpired(currentMillis)) &#123;</span><br><span class="line">                        selectionKey.cancel();</span><br><span class="line">                        registerTask.callback.onConnectTimeout();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                <span class="comment">// eat and log exception</span></span><br><span class="line">                <span class="keyword">if</span> (LOG.isLoggable(Level.FINE)) &#123;</span><br><span class="line">                    LOG.fine(<span class="string">"error occured by performing timeout check task "</span> + e.toString());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　可以看到，最后是在内部类RegisterTask中完成了注册事件。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/02/nio/9.xSocket系列二之源码分析一/" data-id="cijzzpv6d004d1cfs5n8ob37z" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/xSocket/">xSocket</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/8.xSocket系列一之入门" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/02/nio/8.xSocket系列一之入门/" class="article-date">
  <time datetime="2016-01-02T00:00:00.000Z" itemprop="datePublished">2016-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/02/nio/8.xSocket系列一之入门/">xSocket系列一之入门</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　相比于mina和netty等框架，xSocket作为一个小而精的nio框架，代码量不大，非常适合入门。对于想探究nio的高性能和可扩展性开发的同学们有着非常好的参考意义，下面我们将通过一个简单的示例（网络示例）带领大家开启xSocket之旅！</p>
<h2 id="u670D_u52A1_u7AEF_u5B9E_u73B0"><a href="#u670D_u52A1_u7AEF_u5B9E_u73B0" class="headerlink" title="服务端实现"></a>服务端实现</h2><p>　　首先我们需要定义一个服务启动类，只涉及到了一个非常核心的类IServer，如下所示：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">XSocketServer</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PORT = <span class="number">8014</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="comment">//注意其构造方法有多个。一般是使用这种构造方法出来的！</span></span><br><span class="line">        <span class="comment">//不过要注意一下java.net.InetAddress这个类的使用在初始化的时候需要捕获异常</span></span><br><span class="line">        <span class="comment">//可能是这个绑定的主机可能不存在之类的异常即UnknowHostNameException</span></span><br><span class="line">        InetAddress address = InetAddress.getByName(<span class="string">"localhost"</span>);</span><br><span class="line">        <span class="comment">//创建一个服务端的对象</span></span><br><span class="line">        IServer srv = <span class="keyword">new</span> Server(address, PORT, <span class="keyword">new</span> ServerHandler());</span><br><span class="line">        <span class="comment">//设置当前的采用的异步模式</span></span><br><span class="line">        srv.setFlushmode(IConnection.FlushMode.ASYNC);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// srv.run();</span></span><br><span class="line">            <span class="comment">// the call will not return</span></span><br><span class="line">            <span class="comment">// ... or start it by using a dedicated thread</span></span><br><span class="line">            srv.start(); <span class="comment">// returns after the server has been started</span></span><br><span class="line">            System.out.println(<span class="string">"服务器"</span> + srv.getLocalAddress() + <span class="string">":"</span> + PORT);</span><br><span class="line">            Map&lt;String, Class&gt; maps = srv.getOptions();</span><br><span class="line">            <span class="keyword">if</span> (maps != <span class="keyword">null</span>) &#123;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">for</span> (Map.Entry&lt;String, Class&gt; entry : maps.entrySet()) &#123;</span><br><span class="line">                    System.out.println(<span class="string">"key= "</span> + entry.getKey() + <span class="string">" value ="</span> + entry.getValue().getName());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">"日志: "</span> + srv.getStartUpLogMessage());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            System.out.println(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　作为一个通信程序，必然还要涉及到一个功能：数据处理，我们定义如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ServerHandler</span> <span class="keyword">implements</span> <span class="title">IDataHandler</span>, <span class="title">IConnectHandler</span>, <span class="title">IIdleTimeoutHandler</span>, <span class="title">IConnectionTimeoutHandler</span>, <span class="title">IDisconnectHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Resource</span></span><br><span class="line">    IServer server;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 即当建立完连接之后可以进行的一些相关操作处理。包括修改连接属性、准备资源、等！</span><br><span class="line">     */</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onConnect</span><span class="params">(INonBlockingConnection nbc)</span> <span class="keyword">throws</span> IOException,</span><br><span class="line">            BufferUnderflowException, MaxReadSizeExceededException </span>&#123;</span><br><span class="line">        String remoteName = nbc.getRemoteAddress().getHostName();</span><br><span class="line">        System.out.println(<span class="string">"服务器信息 : 客户端  "</span> + remoteName + <span class="string">" 已经连接..."</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 即如果失去连接应当如何处理？需要实现 IDisconnectHandler  这个接口</span><br><span class="line">     */</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onDisconnect</span><span class="params">(INonBlockingConnection nbc)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        String remoteName = nbc.getRemoteAddress().getHostName();</span><br><span class="line">        System.out.println(<span class="string">"服务器信息:客户端 "</span> + remoteName + <span class="string">" 已经断开."</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 即这个方法不光是说当接收到一个新的网络包的时候会调用而且如果有新的缓存存在的时候也会被调用。而且</span><br><span class="line">     * The onData will also be called, if the connection is closed当连接被关闭的时候也会被调用的!</span><br><span class="line">     */</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onData</span><span class="params">(INonBlockingConnection nbc)</span> <span class="keyword">throws</span> IOException,</span><br><span class="line">            BufferUnderflowException, ClosedChannelException,</span><br><span class="line">            MaxReadSizeExceededException </span>&#123;</span><br><span class="line">        String data = nbc.readStringByDelimiter(<span class="string">"|"</span>);</span><br><span class="line">        nbc.write(<span class="string">"--|server:receive data from client sucessful| -----"</span>);</span><br><span class="line">        nbc.flush();</span><br><span class="line">        System.out.println(data);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 请求处理超时的处理事件</span><br><span class="line">     */</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onIdleTimeout</span><span class="params">(INonBlockingConnection connection)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"客户端与服务端已长时间未通信，服务端将强制断开该客户端的连接"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 连接超时处理事件</span><br><span class="line">     */</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onConnectionTimeout</span><span class="params">(INonBlockingConnection connection)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　在这里ServerHandler实现了多个接口，每个接口用于处理不同事件，如：新连接、数据处理以及IO连接闲置事件等。</p>
<h2 id="u5BA2_u6237_u7AEF_u5B9E_u73B0"><a href="#u5BA2_u6237_u7AEF_u5B9E_u73B0" class="headerlink" title="客户端实现"></a>客户端实现</h2><p>　　我们再看看客户端的启动代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">XSocketClient</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PORT = <span class="number">8014</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">//采用非阻塞式的连接</span></span><br><span class="line">        INonBlockingConnection nbc = <span class="keyword">new</span> NonBlockingConnection(<span class="string">"localhost"</span>, PORT, <span class="keyword">new</span> ClientHandler());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//采用阻塞式的连接</span></span><br><span class="line">        <span class="comment">//IBlockingConnection bc = new BlockingConnection("localhost", PORT);</span></span><br><span class="line">        <span class="comment">//一个非阻塞的连接是很容易就变成一个阻塞连接</span></span><br><span class="line">        IBlockingConnection bc = <span class="keyword">new</span> BlockingConnection(nbc);</span><br><span class="line">        <span class="comment">//设置编码格式</span></span><br><span class="line">        bc.setEncoding(<span class="string">"UTF-8"</span>);</span><br><span class="line">        <span class="comment">//设置是否自动清空缓存</span></span><br><span class="line">        bc.setAutoflush(<span class="keyword">true</span>);</span><br><span class="line">        <span class="comment">//向服务端写数据信息</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">            bc.write(<span class="string">" client "</span> + i + <span class="string">" love china !...|"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//向客户端读取数据的信息</span></span><br><span class="line">        <span class="keyword">byte</span>[] byteBuffers = bc.readBytesByDelimiter(<span class="string">"|"</span>, <span class="string">"UTF-8"</span>);</span><br><span class="line">        System.out.println(<span class="keyword">new</span> String(byteBuffers));</span><br><span class="line">        <span class="comment">//将信息清除缓存，写入服务器端</span></span><br><span class="line">        bc.flush();</span><br><span class="line">        bc.close();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　与服务端类似，这里也需要定义一个处理各种事件的类，定义如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClientHandler</span> <span class="keyword">implements</span> <span class="title">IDataHandler</span>, <span class="title">IConnectHandler</span>, <span class="title">IDisconnectHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 连接的成功时的操作</span><br><span class="line">     */</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onConnect</span><span class="params">(INonBlockingConnection nbc)</span> <span class="keyword">throws</span> IOException,</span><br><span class="line">            BufferUnderflowException, MaxReadSizeExceededException </span>&#123;</span><br><span class="line">        String remoteName = nbc.getRemoteAddress().getHostName();</span><br><span class="line">        System.out.println(<span class="string">"remoteName "</span> + remoteName + <span class="string">" has connected ！"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 连接断开时的操作</span><br><span class="line">     */</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onDisconnect</span><span class="params">(INonBlockingConnection nbc)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">// TODO Auto-generated method stub</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 接收到数据库时候的处理</span><br><span class="line">     */</span></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onData</span><span class="params">(INonBlockingConnection nbc)</span> <span class="keyword">throws</span> IOException,</span><br><span class="line">            BufferUnderflowException, ClosedChannelException,</span><br><span class="line">            MaxReadSizeExceededException </span>&#123;</span><br><span class="line">        String data = nbc.readStringByDelimiter(<span class="string">"|"</span>);</span><br><span class="line">        nbc.write(<span class="string">"--|Client:receive data from server sucessful| -----"</span>);</span><br><span class="line">        nbc.flush();</span><br><span class="line">        System.out.println(data);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　运行即可看到结果</p>
<h2 id="u603B_u7ED3"><a href="#u603B_u7ED3" class="headerlink" title="总结"></a>总结</h2><p>　　到这里我们就完成了示例的过程，后续系列文章，我将对服务端的启动过程、客户端连接过程进行详细讲解！</p>
<h2 id="u53C2_u8003_u8D44_u6599"><a href="#u53C2_u8003_u8D44_u6599" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="http://www.oschina.net/code/snippet_74323_4406" target="_blank" rel="external">http://www.oschina.net/code/snippet_74323_4406</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/02/nio/8.xSocket系列一之入门/" data-id="cijzzpv6g004j1cfsf18inkb7" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/xSocket/">xSocket</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-java/9.SiteMesh技术" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2016/01/01/java/9.SiteMesh技术/" class="article-date">
  <time datetime="2015-12-31T17:00:00.000Z" itemprop="datePublished">2016-01-01</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/java/">java</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2016/01/01/java/9.SiteMesh技术/">SiteMesh技术</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　SiteMesh 是一个网页布局和修饰的框架，利用它可以将网页的内容和页面结构分离，以达到页面结构共享的目的。经常用来在JSP中实现页面布局和装饰（layout and decoration）的框架组件，能够帮助网站开发人员较容易实现页面中动态内容和静态装饰外观的分离。</p>
<h2 id="u5DE5_u4F5C_u539F_u7406"><a href="#u5DE5_u4F5C_u539F_u7406" class="headerlink" title="工作原理"></a>工作原理</h2><p>　　SiteMesh是基于Servlet的filter的，即过滤流。它是通过截取response，并进行装饰后再交付给客户。</p>
<h2 id="u4F7F_u7528"><a href="#u4F7F_u7528" class="headerlink" title="使用"></a>使用</h2><p>　　新建web工程，将sitemesh-2.4.1.jar复制到WEB-INF\lib目录下<br>　　修改web.xml，使其能处理所有的请求：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="title">filter</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="title">filter-name</span>&gt;</span>sitemesh<span class="tag">&lt;/<span class="title">filter-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="title">filter-class</span>&gt;</span>com.opensymphony.module.sitemesh.filter.PageFilter<span class="tag">&lt;/<span class="title">filter-class</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="title">filter</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="title">filter-mapping</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="title">filter-name</span>&gt;</span>sitemesh<span class="tag">&lt;/<span class="title">filter-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="title">url-pattern</span>&gt;</span>/*<span class="tag">&lt;/<span class="title">url-pattern</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="title">filter-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>　　在Jsp页面中使用SiteMesh的标签库。还要导入SiteMesh的标签库，SiteMesh提供了两个标签库，一个是page还有一个是decorator<br>　　在WEB-INF下创建一个decorators.xml文件,内容如下<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="pi">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="title">decorators</span> <span class="attribute">defaultdir</span>=<span class="value">"/decorators"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="title">excludes</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="title">pattern</span>&gt;</span>/includes/*<span class="tag">&lt;/<span class="title">pattern</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="title">pattern</span>&gt;</span>/40*.jsp<span class="tag">&lt;/<span class="title">pattern</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="title">pattern</span>&gt;</span>/dwr/*<span class="tag">&lt;/<span class="title">pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="title">excludes</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="title">decorator</span> <span class="attribute">name</span>=<span class="value">"default"</span> <span class="attribute">page</span>=<span class="value">"default.jsp"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="title">pattern</span>&gt;</span>/*<span class="tag">&lt;/<span class="title">pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="title">decorator</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="title">decorators</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>　　这是定义了模板页，也就是所有页面在返回给客户端之前，先在这里加上装饰，套上模板。defaultdir=”/decorators”说明了模板页的路径。<decorator name="default" page="default.jsp">模板页的名称。    <pattern>/*</pattern>表示对所有的response进行处理<br>　　在web下面建一个文件夹取名decorators。在decorators下面创建上面定义的模板页面default.jsp,内容如下:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="title">%@</span> <span class="attribute">page</span> <span class="attribute">language</span>=<span class="value">"java"</span> <span class="attribute">import</span>=<span class="value">"java.util.*"</span> <span class="attribute">pageEncoding</span>=<span class="value">"UTF-8"</span><span class="value">%</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="title">%@</span> <span class="attribute">taglib</span> <span class="attribute">uri</span>=<span class="value">"http://www.opensymphony.com/sitemesh/decorator"</span> <span class="attribute">prefix</span>=<span class="value">"decorator"</span><span class="value">%</span>&gt;</span> </span><br><span class="line"><span class="doctype">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="title">html</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="title">head</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="title">title</span>&gt;</span><span class="tag">&lt;<span class="title">decorator:title</span> /&gt;</span><span class="tag">&lt;/<span class="title">title</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;<span class="title">body</span>&gt;</span> </span><br><span class="line">        <span class="tag">&lt;<span class="title">p</span>&gt;</span>Add head decorator...<span class="tag">&lt;/<span class="title">p</span>&gt;</span> </span><br><span class="line">        <span class="tag">&lt;<span class="title">decorator:body</span> /&gt;</span> </span><br><span class="line">        <span class="tag">&lt;<span class="title">p</span>&gt;</span>Add foot decorator...<span class="tag">&lt;/<span class="title">p</span>&gt;</span> </span><br><span class="line">    <span class="tag">&lt;/<span class="title">body</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="title">html</span>&gt;</span></span><br></pre></td></tr></table></figure></decorator></p>
<p>　　说明:<br>　　1. &lt;%@ taglib uri=”<a href="http://www.opensymphony.com/sitemesh/decorator" target="_blank" rel="external">http://www.opensymphony.com/sitemesh/decorator</a>“ prefix=”decorator”%&gt; 此处为decorator标签的声明。因为我们下面要使用到它<br>　　2. <decorator:title>把请求的原始页面的title内容插入到<title></title>,比如我们要请求index.jsp页面的时候。会把index.jsp中的title的内容放入到这里<br>　　3. <decorator:body>把请求的原始页面的body内容插入到<body></body>，我们在这句的前面加上了&lt;p&gt;Add head decorator…&lt;/p&gt;和&lt;p&gt;Add foot decorator…&lt;/p&gt;，相当于给我们请求的页面的body内容加上了头部和尾部.实现了模板功能。</decorator:body></decorator:title></p>
<p>　　在web下创建我们要请求访问的页面index.jsp，内容如下:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="title">%@</span> <span class="attribute">page</span> <span class="attribute">language</span>=<span class="value">"java"</span> <span class="attribute">contentType</span>=<span class="value">"text/html; charset=ISO-8859-1"</span> </span><br><span class="line"><span class="attribute">pageEncoding</span>=<span class="value">"ISO-8859-1"</span><span class="value">%</span>&gt;</span> </span><br><span class="line"><span class="doctype">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="title">html</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="title">head</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="title">title</span>&gt;</span>SiteMesh Sample Site<span class="tag">&lt;/<span class="title">title</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="title">head</span>&gt;</span> </span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="title">body</span>&gt;</span> </span><br><span class="line">Welcome to the SiteMesh sample... </span><br><span class="line"><span class="tag">&lt;/<span class="title">body</span>&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="title">html</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>　　把web工程部署到tomcat容器中。输入<a href="http://localhost:8080/SiteMeshDemo/" target="_blank" rel="external">http://localhost:8080/SiteMeshDemo/</a></p>
<p>页面效果如下：<br>Add head decorator…<br>Welcome to the SiteMesh sample…<br>Add foot decorator…</p>
<p>　　不难发现，我们index.jsp中只有Welcome to the SiteMesh sample… 一句。但是在返回给我们之前套上了　default.jsp　模板页。在它的前面和后面分别加上了一句话。通过Sitemesh我们可以很容易实现页面中动态内容和静态装饰外观的分离。源码链接：<a href="/code/SiteMeshDemo.zip">SiteMeshDemo.zip</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2016/01/01/java/9.SiteMesh技术/" data-id="cijzzpvd000bw1cfs1n6accww" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/sitemesh/">sitemesh</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-spring/1.spring boot多数据源" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2015/12/31/spring/1.spring boot多数据源/" class="article-date">
  <time datetime="2015-12-30T17:00:00.000Z" itemprop="datePublished">2015-12-31</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/spring/">spring</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2015/12/31/spring/1.spring boot多数据源/">spring boot多数据源</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　spring boot 作为一个越来越流行的微服务框架，收到了广大程序员的欢迎。随着使用场景越来越多，遇到的问题也就越来越多。这里我们将介绍一下spring boot的多数据源的实践，下面的示例我们实现了两个JdbcTemple和一个mybatis数据源的配置！</p>
<h2 id="u914D_u7F6E_u6570_u636E_u6E90"><a href="#u914D_u7F6E_u6570_u636E_u6E90" class="headerlink" title="配置数据源"></a>配置数据源</h2><p>　　在spring boot中，所有的配置都可以在application.properties中完成，数据源的配置也是如此!<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">#数据源1</span><br><span class="line">spring.ds_items.driverClassName=com.mysql.jdbc.Driver</span><br><span class="line">spring.ds_items.url=jdbc:mysql://localhost:3306/demo1</span><br><span class="line">spring.ds_items.username=root</span><br><span class="line">spring.ds_items.password=123456</span><br><span class="line"></span><br><span class="line">#数据源2</span><br><span class="line">spring.ds_users.driverClassName=com.mysql.jdbc.Driver</span><br><span class="line">spring.ds_users.url=jdbc:mysql://localhost:3306/demo2</span><br><span class="line">spring.ds_users.username=root</span><br><span class="line">spring.ds_users.password=123456</span><br><span class="line"></span><br><span class="line">#数据源3</span><br><span class="line">spring.ds_fruit.driverClassName=com.mysql.jdbc.Driver</span><br><span class="line">spring.ds_fruit.url=jdbc:mysql://localhost:3306/demo3</span><br><span class="line">spring.ds_fruit.username=root</span><br><span class="line">spring.ds_fruit.password=123456</span><br></pre></td></tr></table></figure></p>
<p>　　这里我们配置了三个数据源类，由于数据库服务器不够，这里三个数据源为同一个服务器的三个数据库，对我们的示例没有什么影响！</p>
<h2 id="u6570_u636E_u6E90"><a href="#u6570_u636E_u6E90" class="headerlink" title="数据源"></a>数据源</h2><p>　　下面我们新建了三个数据源配置类，通过配置文件三个数据源的不同前缀来保证加载不同的数据源配置。三个数据源分配命名为：primaryDS、secondaryDS和thirdDS，注意第一个数据源多了一个注解@Primary。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@Configuration</span></span><br><span class="line"><span class="comment">//@ConfigurationProperties(prefix = "spring.ds_items")</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DatabaseItemsConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Bean</span>(name = <span class="string">"primaryDS"</span>)</span><br><span class="line">    <span class="annotation">@Primary</span> <span class="comment">//默认数据源</span></span><br><span class="line">    <span class="annotation">@ConfigurationProperties</span>(prefix=<span class="string">"spring.ds_items"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> DataSource <span class="title">primaryDataSource</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DataSourceBuilder.create().build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Bean</span>(name = <span class="string">"jdbcItems"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> JdbcTemplate <span class="title">jdbcTemplate</span><span class="params">(@Qualifier(<span class="string">"primaryDS"</span>)</span>DataSource dsItems) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> JdbcTemplate(dsItems);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="annotation">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DatabaseUsersConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Bean</span>(name = <span class="string">"secondaryDS"</span>)</span><br><span class="line">    <span class="annotation">@ConfigurationProperties</span>(prefix=<span class="string">"spring.ds_users"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> DataSource <span class="title">primaryDataSource</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DataSourceBuilder.create().build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Bean</span>(name = <span class="string">"jdbcUsers"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> JdbcTemplate <span class="title">jdbcTemplate</span><span class="params">(@Qualifier(<span class="string">"secondaryDS"</span>)</span>DataSource dsItems) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> JdbcTemplate(dsItems);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="annotation">@Configuration</span></span><br><span class="line"><span class="annotation">@MapperScan</span>(basePackages =<span class="string">"com.shc.spring.boot.mapper"</span>,</span><br><span class="line">        sqlSessionFactoryRef=<span class="string">"mybatisSessionFactory"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MybatisDBConfig</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Bean</span>(name = <span class="string">"thirdDS"</span>)</span><br><span class="line">    <span class="annotation">@ConfigurationProperties</span>(prefix=<span class="string">"spring.ds_fruit"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> DataSource <span class="title">thirdDataSource</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> DataSourceBuilder.create().build();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Bean</span>(name=<span class="string">"mybatisSessionFactory"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> SqlSessionFactory <span class="title">sqlSessionFactory</span><span class="params">(@Qualifier(<span class="string">"thirdDS"</span>)</span>DataSource datasource) <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">final</span> SqlSessionFactoryBean sessionFactory = <span class="keyword">new</span> SqlSessionFactoryBean();</span><br><span class="line">        sessionFactory.setDataSource(datasource);</span><br><span class="line">        sessionFactory.setConfigLocation(<span class="keyword">new</span> ClassPathResource(<span class="string">"mybatis-config.xml"</span>));</span><br><span class="line">        <span class="keyword">return</span> sessionFactory.getObject();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="u6570_u636E_u8BBF_u95EE_u5C42"><a href="#u6570_u636E_u8BBF_u95EE_u5C42" class="headerlink" title="数据访问层"></a>数据访问层</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ItemRepository</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Autowired</span></span><br><span class="line">    <span class="annotation">@Qualifier</span>(<span class="string">"jdbcItems"</span>)</span><br><span class="line">    <span class="keyword">protected</span> JdbcTemplate jdbcItems;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Autowired</span></span><br><span class="line">    <span class="annotation">@Qualifier</span>(<span class="string">"jdbcUsers"</span>)</span><br><span class="line">    <span class="keyword">protected</span> JdbcTemplate jdbcUsers;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Item <span class="title">getItem</span><span class="params">(<span class="keyword">long</span> id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> jdbcItems.queryForObject(<span class="string">"SELECT * FROM t_item WHERE f_id=?"</span>, itemMapper, id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> RowMapper&lt;Item&gt; itemMapper = <span class="keyword">new</span> RowMapper&lt;Item&gt;() &#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> Item <span class="title">mapRow</span><span class="params">(ResultSet rs, <span class="keyword">int</span> rowNum)</span> <span class="keyword">throws</span> SQLException </span>&#123;</span><br><span class="line">            Item item = <span class="keyword">new</span> Item(rs.getLong(<span class="string">"f_id"</span>), rs.getString(<span class="string">"f_title"</span>));</span><br><span class="line">            <span class="keyword">return</span> item;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> User <span class="title">getUser</span><span class="params">(<span class="keyword">long</span> id)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> jdbcUsers.queryForObject(<span class="string">"SELECT * FROM t_user WHERE f_id=?"</span>, userMapper, id);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> RowMapper&lt;User&gt; userMapper = <span class="keyword">new</span> RowMapper&lt;User&gt;() &#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> User <span class="title">mapRow</span><span class="params">(ResultSet rs, <span class="keyword">int</span> rowNum)</span> <span class="keyword">throws</span> SQLException </span>&#123;</span><br><span class="line">            User user = <span class="keyword">new</span> User(rs.getLong(<span class="string">"f_id"</span>), rs.getString(<span class="string">"f_name"</span>));</span><br><span class="line">            <span class="keyword">return</span> user;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>　　mybatis的配置这里不再贴出，最后看看启动程序！</p>
<h2 id="u542F_u52A8"><a href="#u542F_u52A8" class="headerlink" title="启动"></a>启动</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="annotation">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DemoApplication</span> <span class="keyword">implements</span> <span class="title">CommandLineRunner</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Autowired</span></span><br><span class="line">    ItemRepository itemRepository;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Autowired</span></span><br><span class="line">    FruitMapper fruitMapper;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SpringApplication springApplication = <span class="keyword">new</span> SpringApplication(DemoApplication.class);</span><br><span class="line">        springApplication.run(args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="annotation">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">(String... args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        Item item = itemRepository.getItem(<span class="number">1</span>);</span><br><span class="line">        System.out.println(item);</span><br><span class="line"></span><br><span class="line">        User user = itemRepository.getUser(<span class="number">1</span>);</span><br><span class="line">        System.out.println(user);</span><br><span class="line"></span><br><span class="line">        Fruit fruit = fruitMapper.selectById(<span class="number">1</span>);</span><br><span class="line">        System.out.println(fruit);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="SQL"><a href="#SQL" class="headerlink" title="SQL"></a>SQL</h1><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="operator"><span class="keyword">use</span> demo1;</span></span><br><span class="line"><span class="operator"><span class="keyword">create</span> <span class="keyword">table</span> t_item(</span><br><span class="line">  f_id <span class="built_in">int</span>,</span><br><span class="line">  f_title <span class="built_in">varchar</span>(<span class="number">32</span>)</span><br><span class="line">);</span></span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> t_item(f_id, f_title)<span class="keyword">values</span>(<span class="number">1</span>, <span class="string">'spring boot book'</span>);</span></span><br><span class="line"></span><br><span class="line"><span class="operator"><span class="keyword">use</span> demo2;</span></span><br><span class="line"><span class="operator"><span class="keyword">create</span> <span class="keyword">table</span> t_user(</span><br><span class="line">  f_id <span class="built_in">int</span>,</span><br><span class="line">  f_name <span class="built_in">varchar</span>(<span class="number">32</span>)</span><br><span class="line">);</span></span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> t_user(f_id, f_name)<span class="keyword">values</span>(<span class="number">1</span>, <span class="string">'learner'</span>);</span></span><br><span class="line"></span><br><span class="line"><span class="operator"><span class="keyword">use</span> demo3;</span></span><br><span class="line"><span class="operator"><span class="keyword">create</span> <span class="keyword">table</span> t_fruit(</span><br><span class="line">  f_id <span class="built_in">int</span>,</span><br><span class="line">  f_name <span class="built_in">varchar</span>(<span class="number">32</span>)</span><br><span class="line">);</span></span><br><span class="line"><span class="operator"><span class="keyword">insert</span> <span class="keyword">into</span> t_fruit(f_id, f_name)<span class="keyword">values</span>(<span class="number">1</span>, <span class="string">'苹果'</span>);</span></span><br></pre></td></tr></table></figure>
<h2 id="u603B_u7ED3"><a href="#u603B_u7ED3" class="headerlink" title="总结"></a>总结</h2><p>　　这里我们就完成了所有的代码，源码下载地址：<a href="/code/spring-boot-multi-datasource.zip">spring-boot-multi-datasource.zip</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2015/12/31/spring/1.spring boot多数据源/" data-id="cijzzpv2f001n1cfsfcd0ujjr" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/spring/">spring</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/7.长连接的一些问题" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2015/12/23/nio/7.长连接的一些问题/" class="article-date">
  <time datetime="2015-12-22T23:00:00.000Z" itemprop="datePublished">2015-12-23</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2015/12/23/nio/7.长连接的一些问题/">长连接的一些问题</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　在即使通信领域，特别是移动端，为了保证流量的小号，基本上都是建立长连接。此时就有一些需要注意的问题：</p>
<h2 id="u9632_u706B_u5899"><a href="#u9632_u706B_u5899" class="headerlink" title="防火墙"></a>防火墙</h2><p>　　系统的防火墙限制了每种协议如tcp、udp、http和telnet等的连接保持时间，我们通过命令：firewall session aging-time 即可看到最大连接时间，这样就导致了即时通信时的掉线问题：<br>　　众所周知，TCP是有状态的连接，状态检测的防火墙能根据该IP包所属的连接是新的还是旧的，决定该IP包是否符合防火墙的政策约定。因此，防火墙必须在内存中保留这一记录，每一个连接，就是一个会话。防火墙支持多少会话连接，取决于防火墙的内存多少，系统会自动使用所有内存，直至内存用光，系统崩溃为止。因此，许多防火墙都会设定一个会话连接最大值，一旦系统记录的会话达到这个数值，系统就不再建立新的会话。同时，为了保证防火墙的会话连接不会过多，防火墙提出的长连接和短连接的概念，针对不同的网络应用协议调整会话连接保持的时间。</p>
<h3 id="u9632_u706B_u5899_u957F_u8FDE_u63A5"><a href="#u9632_u706B_u5899_u957F_u8FDE_u63A5" class="headerlink" title="防火墙长连接"></a>防火墙长连接</h3><p>　　长连接功能用于设置特定数据流的超长保持时间，让数据流的会话连接保持时间不受全局老化时间限制。其实这项特殊业务与目前业<br>界的状态防火墙的实现机制是存在矛盾的。为保证内部网络的安全，防火墙上的各会话缺省保持时间都相对较短，例如：缺省情况下，TCP的保持时间为1200s，UDP的保持时间为120s。<br>　　正常情况下，当一个TCP会话的两个连续报文到达防火墙的时间间隔大于该会话的保持时间时，为保证网络的安全性，防火墙将从会话表中删除相应会话信息。后续报文到达防火墙后，防火墙根据自身的转发机制，丢弃该报文，导致连接中断。在实际应用中，用户需要查询服务器上的数据，这些查询时间间隔远大于TCP/UDP默认的会话保持时间。此时需要在防火墙上保持TCP连接一段相对较长的时间。当某会话的报文长时间没有到达防火墙后再次到达时，仍然能够通过防火墙。这种技术就是长连接。</p>
<h3 id="u9632_u706B_u5899_u77ED_u8FDE_u63A5"><a href="#u9632_u706B_u5899_u77ED_u8FDE_u63A5" class="headerlink" title="防火墙短连接"></a>防火墙短连接</h3><p>　　某些应用频繁发起连接，如果不缩短其会话保持时间，则会使防火墙的会话数爆涨，进而拖垮防火墙。保持太多的会话对防火墙没有必要，相反，当系统资源过多地用在会话保持的话，会相应损害每秒生成会话的能力，这是一个同样重要的性能指标。设定过高的会话数量，却降低了每秒生成会话的能力，其结果，只能是保留一些永远用不到的会话虚数而已。<br>　　因此，我们可以根据网络应用环境的实际需求，缩短某些会话的保持时间，从而减少防火墙的工作负荷，提高网络性能。</p>
<p>```</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2015/12/23/nio/7.长连接的一些问题/" data-id="cijzzpv6j004n1cfsrf6of60e" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/socket/">socket</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-nio/6.SocketChannel在线聊天" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2015/12/23/nio/6.SocketChannel在线聊天/" class="article-date">
  <time datetime="2015-12-22T22:00:00.000Z" itemprop="datePublished">2015-12-23</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2015/12/23/nio/6.SocketChannel在线聊天/">SocketChannel在线聊天</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="u7B80_u4ECB"><a href="#u7B80_u4ECB" class="headerlink" title="简介"></a>简介</h2><p>　　现在移动端大火，移动端即时通信的需求量很大。作为半个android开发人员，或多或少的都会接触到即使通信的需求，前几年用的比较多的就是openfire，还有androidpn。这里先不对他们做原理分析，而是通过下面一个简单的示例实现最基础的通信功能，让大家对采用NIO实现即时通信的原理和方法有个基本的认识。<br>　　下面的例子为网上的一个示例，之后我会分析该示例的一些问题，最后修改该示例，解决这些问题，仅供大家参考。</p>
<h2 id="u670D_u52A1_u7AEF"><a href="#u670D_u52A1_u7AEF" class="headerlink" title="服务端"></a>服务端</h2><p>　　上一篇的示例中我们介绍了ServerSocketChannel最基本的用法，每次文件传输都建立新的连接。在这个示例中服务端与客户端将保持长连接，反复进行消息发送和接受。这里我们不再单独对代码做讲解，请直接看代码里面的注释，最后看文章后面的分析。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ChatRoomServer</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Selector selector = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> port = <span class="number">9999</span>;</span><br><span class="line">    <span class="keyword">private</span> Charset charset = Charset.forName(<span class="string">"UTF-8"</span>);</span><br><span class="line">    <span class="comment">//用来记录在线人数，以及昵称</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> HashSet&lt;String&gt; users = <span class="keyword">new</span> HashSet&lt;String&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String USER_EXIST = <span class="string">"system message: user exist, please change a name"</span>;</span><br><span class="line">    <span class="comment">//相当于自定义协议格式，与客户端协商好</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String USER_CONTENT_SPILIT = <span class="string">"#@#"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">boolean</span> flag = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        selector = Selector.open();</span><br><span class="line">        ServerSocketChannel server = ServerSocketChannel.open();</span><br><span class="line">        server.bind(<span class="keyword">new</span> InetSocketAddress(port));</span><br><span class="line">        server.configureBlocking(<span class="keyword">false</span>);</span><br><span class="line">        <span class="comment">//注册到选择器上，设置为监听状态</span></span><br><span class="line">        server.register(selector, SelectionKey.OP_ACCEPT);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"Server is listening now..."</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>) &#123;</span><br><span class="line">            <span class="keyword">int</span> readyChannels = selector.select();</span><br><span class="line">            <span class="keyword">if</span>(readyChannels == <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line">            Set&lt;SelectionKey&gt; selectedKeys = selector.selectedKeys();  <span class="comment">//可以通过这个方法，知道可用通道的集合</span></span><br><span class="line">            Iterator&lt;SelectionKey&gt; keyIterator = selectedKeys.iterator();</span><br><span class="line">            <span class="keyword">while</span>(keyIterator.hasNext()) &#123;</span><br><span class="line">                SelectionKey sk = keyIterator.next();</span><br><span class="line">                keyIterator.remove();</span><br><span class="line">                dealWithSelectionKey(server, sk);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">dealWithSelectionKey</span><span class="params">(ServerSocketChannel server, SelectionKey sk)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">//ServerSocketChannel注册为OP_ACCEPT</span></span><br><span class="line">        <span class="keyword">if</span>(sk.isAcceptable()) &#123;</span><br><span class="line">            SocketChannel sc = server.accept();</span><br><span class="line">            <span class="comment">//非阻塞模式</span></span><br><span class="line">            sc.configureBlocking(<span class="keyword">false</span>);</span><br><span class="line">            <span class="comment">// 注册选择器，并设置为读取模式，收到一个连接请求，</span></span><br><span class="line">            <span class="comment">// 然后起一个SocketChannel，并注册到selector上，之后这个连接的数据，就由这个SocketChannel处理</span></span><br><span class="line">            sc.register(selector, SelectionKey.OP_READ);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//将此对应的channel设置为准备接受其他客户端请求</span></span><br><span class="line">            <span class="comment">//sk.interestOps(SelectionKey.OP_ACCEPT);</span></span><br><span class="line">            System.out.println(<span class="string">"Server is listening from client :"</span> + sc.getRemoteAddress());</span><br><span class="line">            sc.write(charset.encode(<span class="string">"Please input your name."</span>));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//ServerSocketChannel注册为OP_ACCEPT，SocketChannel注册为OP_READ</span></span><br><span class="line">        <span class="keyword">if</span>(sk.isReadable()) &#123;</span><br><span class="line">            <span class="comment">//返回该SelectionKey对应的 Channel，其中有数据需要读取</span></span><br><span class="line">            SocketChannel sc = (SocketChannel)sk.channel();</span><br><span class="line">            ByteBuffer buff = ByteBuffer.allocate(<span class="number">1024</span>);</span><br><span class="line">            StringBuilder content = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">while</span>(sc.read(buff) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    buff.flip();</span><br><span class="line">                    content.append(charset.decode(buff));</span><br><span class="line">                &#125;</span><br><span class="line">                System.out.println(<span class="string">"Server is listening from client "</span> + sc.getRemoteAddress() + <span class="string">" data rev is: "</span> + content);</span><br><span class="line">                <span class="comment">//将此对应的channel设置为准备下一次接受数据</span></span><br><span class="line">                sk.interestOps(SelectionKey.OP_READ);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException io) &#123;</span><br><span class="line">                sk.cancel();</span><br><span class="line">                <span class="keyword">if</span>(sk.channel() != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    sk.channel().close();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(content.length() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                String[] arrayContent = content.toString().split(USER_CONTENT_SPILIT);</span><br><span class="line">                <span class="comment">//注册用户</span></span><br><span class="line">                <span class="keyword">if</span>(arrayContent != <span class="keyword">null</span> &amp;&amp; arrayContent.length ==<span class="number">1</span>) &#123;</span><br><span class="line">                    String name = arrayContent[<span class="number">0</span>];</span><br><span class="line">                    <span class="keyword">if</span>(users.contains(name)) &#123;</span><br><span class="line">                        sc.write(charset.encode(USER_EXIST));</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        users.add(name);</span><br><span class="line">                        <span class="keyword">int</span> num = OnlineNum(selector);</span><br><span class="line">                        String message = <span class="string">"welcome "</span>+name+<span class="string">" to chat room! Online numbers:"</span>+num;</span><br><span class="line">                        BroadCast(selector, <span class="keyword">null</span>, message);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span>(arrayContent != <span class="keyword">null</span> &amp;&amp; arrayContent.length &gt;<span class="number">1</span>) &#123;</span><br><span class="line">                    <span class="comment">//注册完了，发送消息</span></span><br><span class="line">                    String name = arrayContent[<span class="number">0</span>];</span><br><span class="line">                    String message = content.substring(name.length()+USER_CONTENT_SPILIT.length());</span><br><span class="line">                    message = name + <span class="string">" say "</span> + message;</span><br><span class="line">                    <span class="keyword">if</span>(users.contains(name)) &#123;</span><br><span class="line">                        <span class="comment">//不回发给发送此内容的客户端</span></span><br><span class="line">                        BroadCast(selector, sc, message);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//TODO 要是能检测下线，就不用这么统计了</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">OnlineNum</span><span class="params">(Selector selector)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(SelectionKey key : selector.keys()) &#123;</span><br><span class="line">            Channel targetchannel = key.channel();</span><br><span class="line">            <span class="keyword">if</span>(targetchannel <span class="keyword">instanceof</span> SocketChannel) &#123;</span><br><span class="line">                res++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">BroadCast</span><span class="params">(Selector selector, SocketChannel except, String content)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">//广播数据到所有的SocketChannel中</span></span><br><span class="line">        <span class="keyword">for</span>(SelectionKey key : selector.keys()) &#123;</span><br><span class="line">            Channel targetchannel = key.channel();</span><br><span class="line">            <span class="comment">//如果except不为空，不回发给发送此内容的客户端</span></span><br><span class="line">            <span class="keyword">if</span>(targetchannel <span class="keyword">instanceof</span> SocketChannel &amp;&amp; targetchannel!=except) &#123;</span><br><span class="line">                SocketChannel dest = (SocketChannel)targetchannel;</span><br><span class="line">                dest.write(charset.encode(content));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> ChatRoomServer().init();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="u5BA2_u6237_u7AEF"><a href="#u5BA2_u6237_u7AEF" class="headerlink" title="客户端"></a>客户端</h2><p>　　上一篇的示例中我们介绍了ServerSocketChannel最基本的用法，每次文件传输都建立新的连接。在这个示例中服务端与客户端将保持长连接，反复进行消息发送和接受。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ChatRoomClient</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Selector selector = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> port = <span class="number">9999</span>;</span><br><span class="line">    <span class="keyword">private</span> Charset charset = Charset.forName(<span class="string">"UTF-8"</span>);</span><br><span class="line">    <span class="keyword">private</span> SocketChannel sc = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">private</span> String name = <span class="string">""</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String USER_EXIST = <span class="string">"system message: user exist, please change a name"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String USER_CONTENT_SPILIT = <span class="string">"#@#"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        selector = Selector.open();</span><br><span class="line">        <span class="comment">//连接远程主机的IP和端口</span></span><br><span class="line">        sc = SocketChannel.open(<span class="keyword">new</span> InetSocketAddress(<span class="string">"127.0.0.1"</span>,port));</span><br><span class="line">        sc.configureBlocking(<span class="keyword">false</span>);</span><br><span class="line">        sc.register(selector, SelectionKey.OP_READ);</span><br><span class="line">        <span class="comment">//开辟一个新线程来读取从服务器端的数据</span></span><br><span class="line">        <span class="keyword">new</span> Thread(<span class="keyword">new</span> ClientThread()).start();</span><br><span class="line">        <span class="comment">//在主线程中 从键盘读取数据输入到服务器端</span></span><br><span class="line">        Scanner scan = <span class="keyword">new</span> Scanner(System.in);</span><br><span class="line">        <span class="keyword">while</span>(scan.hasNextLine()) &#123;</span><br><span class="line">            String line = scan.nextLine();</span><br><span class="line">            <span class="keyword">if</span>(<span class="string">""</span>.equals(line)) <span class="keyword">continue</span>; <span class="comment">//不允许发空消息</span></span><br><span class="line">            <span class="keyword">if</span>(<span class="string">""</span>.equals(name)) &#123;</span><br><span class="line">                name = line;</span><br><span class="line">                line = name+USER_CONTENT_SPILIT;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                line = name + USER_CONTENT_SPILIT + line;</span><br><span class="line">            &#125;</span><br><span class="line">            sc.write(charset.encode(line));</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ClientThread</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">while</span>(<span class="keyword">true</span>) &#123;</span><br><span class="line">                    <span class="keyword">int</span> readyChannels = selector.select();</span><br><span class="line">                    <span class="keyword">if</span>(readyChannels == <span class="number">0</span>) <span class="keyword">continue</span>;</span><br><span class="line">                    Set selectedKeys = selector.selectedKeys();  <span class="comment">//可以通过这个方法，知道可用通道的集合</span></span><br><span class="line">                    Iterator keyIterator = selectedKeys.iterator();</span><br><span class="line">                    <span class="keyword">while</span>(keyIterator.hasNext()) &#123;</span><br><span class="line">                        SelectionKey sk = (SelectionKey) keyIterator.next();</span><br><span class="line">                        keyIterator.remove();</span><br><span class="line">                        dealWithSelectionKey(sk);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">catch</span> (IOException io) &#123;&#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">dealWithSelectionKey</span><span class="params">(SelectionKey sk)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">            <span class="keyword">if</span>(sk.isReadable()) &#123;</span><br><span class="line">                <span class="comment">//使用 NIO 读取 Channel中的数据，这个和全局变量sc是一样的，因为只注册了一个SocketChannel</span></span><br><span class="line">                <span class="comment">//sc既能写也能读，这边是读</span></span><br><span class="line">                SocketChannel sc = (SocketChannel)sk.channel();</span><br><span class="line"></span><br><span class="line">                ByteBuffer buff = ByteBuffer.allocate(<span class="number">1024</span>);</span><br><span class="line">                String content = <span class="string">""</span>;</span><br><span class="line">                <span class="keyword">while</span>(sc.read(buff) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    buff.flip();</span><br><span class="line">                    content += charset.decode(buff);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//若系统发送通知名字已经存在，则需要换个昵称</span></span><br><span class="line">                <span class="keyword">if</span>(USER_EXIST.equals(content)) &#123;</span><br><span class="line">                    name = <span class="string">""</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                System.out.println(content);</span><br><span class="line">                sk.interestOps(SelectionKey.OP_READ);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> ChatRoomClient().init();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="u5206_u6790"><a href="#u5206_u6790" class="headerlink" title="分析"></a>分析</h2><p>　　上面的例子实现了基本的消息通信功能，但是也存在一些问题<br>　　1. 无法进行定向发送<br>　　2. 无法检测到客户端的掉线<br>　　3. 只有最基本的信息交换协议和解析过程，不能方便的协议扩展，即无法支持不同类型的消息<br>　　4. 未处理粘包掉包的问题<br>　　5. 还有一个隐藏最深的问题就是如果服务端的dealWithSelectionKey方法执行的太耗时，则服务端无法迅速的处理新的连接请求和消息发送请求，这里就涉及到了经典的reactor模式。</p>
<h2 id="u89E3_u51B3"><a href="#u89E3_u51B3" class="headerlink" title="解决"></a>解决</h2><p>　　根据上面的例子，我们对示例进行了改造。为方便起见，这里定义了一个实体类，代表了客户端和服务端的信息交换。同时涉及到io的读写，我也写了一个简单的工具类，如下所示：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Message</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span><br><span class="line">     * 消息类型&lt;br/&gt;</span><br><span class="line">     * 1：登录 此时data属性代表用户名&lt;br/&gt;</span><br><span class="line">     * 2：发送消息 此时data表示消息体，receiver代表接收人，如果为空，则标识发送到所有用户</span><br><span class="line">     * 3：下线 此时data表示下面用户名</span><br><span class="line">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> type;</span><br><span class="line">    <span class="keyword">private</span> String data;</span><br><span class="line">    <span class="keyword">private</span> String receiver;</span><br><span class="line">    <span class="keyword">private</span> String sender;</span><br><span class="line">    <span class="comment">//getters and setters</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">IOUtil</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ByteBuffer <span class="title">obj2ByteBuffer</span><span class="params">(Message msg)</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        ByteArrayOutputStream bOut = <span class="keyword">new</span> ByteArrayOutputStream();</span><br><span class="line">        ObjectOutputStream out = <span class="keyword">new</span> ObjectOutputStream(bOut);</span><br><span class="line">        out.writeObject(msg);</span><br><span class="line">        out.flush();</span><br><span class="line">        <span class="keyword">return</span> ByteBuffer.wrap(bOut.toByteArray());</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Message <span class="title">byteBuffer2Obj</span><span class="params">(ByteBuffer byteBuffer)</span> <span class="keyword">throws</span> IOException, ClassNotFoundException </span>&#123;</span><br><span class="line">        ObjectInputStream ois =  <span class="keyword">new</span> ObjectInputStream(<span class="keyword">new</span> ByteArrayInputStream(byteBuffer.array()));</span><br><span class="line">        <span class="keyword">return</span> (Message)ois.readObject();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　在服务端的代码中，主要增加了变量nameChannel，用于记录用户与SelectionKey的对应关系，同时也增加了方法parseMsg，用于将服务端收到的消息进行解析处理，如下所示：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> HashMap&lt;String, SelectionKey&gt; nameChannel = <span class="keyword">new</span> HashMap&lt;String, SelectionKey&gt;();</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">parseMsg</span><span class="params">(SocketChannel sc, SelectionKey sk)</span></span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        ByteBuffer buff = ByteBuffer.allocate(<span class="number">1024</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span>(sc.read(buff) &gt; <span class="number">0</span>) &#123;&#125;</span><br><span class="line">            <span class="comment">//将此对应的channel设置为准备下一次接受数据</span></span><br><span class="line">            sk.interestOps(SelectionKey.OP_READ);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException io) &#123;</span><br><span class="line">            sk.cancel();</span><br><span class="line">            <span class="keyword">if</span>(sk.channel() != <span class="keyword">null</span>) &#123;</span><br><span class="line">                sk.channel().close();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        buff.flip();</span><br><span class="line">        Message msg = IOUtil.byteBuffer2Obj(buff);</span><br><span class="line">        <span class="keyword">if</span>(msg.getType() == <span class="number">1</span>)&#123;<span class="comment">//登录</span></span><br><span class="line">            String name = msg.getData();</span><br><span class="line">            <span class="comment">//注意此处的用法，在后面会通过sk.attachment获取到用户名</span></span><br><span class="line">            sk.attach(name);</span><br><span class="line">            <span class="comment">//用户登录，则放入nameChannel中</span></span><br><span class="line">            nameChannel.put(name, sk);</span><br><span class="line">            Iterator&lt;Map.Entry&lt;String, SelectionKey&gt;&gt; iterator = nameChannel.entrySet().iterator();</span><br><span class="line">            <span class="keyword">while</span> (iterator.hasNext())&#123;</span><br><span class="line">                SocketChannel socketChannel = (SocketChannel)(iterator.next().getValue().channel());</span><br><span class="line">                <span class="keyword">if</span>(sc != socketChannel)&#123;<span class="comment">//登录信息不发给自己</span></span><br><span class="line">                    socketChannel.write(IOUtil.obj2ByteBuffer(msg));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;<span class="keyword">else</span> <span class="keyword">if</span>(msg.getType() == <span class="number">2</span>)&#123;<span class="comment">//发消息</span></span><br><span class="line">            String receiver = msg.getReceiver();</span><br><span class="line">            msg.setSender((String)sk.attachment());</span><br><span class="line">            <span class="keyword">if</span>(receiver != <span class="keyword">null</span> &amp;&amp; nameChannel.get(receiver) != <span class="keyword">null</span>)&#123;<span class="comment">//向指定用户发送</span></span><br><span class="line">                SocketChannel socketChannel = (SocketChannel)(nameChannel.get(receiver).channel());</span><br><span class="line">                socketChannel.write(IOUtil.obj2ByteBuffer(msg));</span><br><span class="line">            &#125;<span class="keyword">else</span> <span class="keyword">if</span>(receiver == <span class="keyword">null</span>)&#123;<span class="comment">//没有接受者，则向所有用户发送</span></span><br><span class="line">                Iterator&lt;Map.Entry&lt;String, SelectionKey&gt;&gt; iterator = nameChannel.entrySet().iterator();</span><br><span class="line">                <span class="keyword">while</span> (iterator.hasNext())&#123;</span><br><span class="line">                    SocketChannel socketChannel = (SocketChannel)(iterator.next().getValue().channel());</span><br><span class="line">                    <span class="keyword">if</span>(sc != socketChannel)&#123;<span class="comment">//不发送给自己</span></span><br><span class="line">                        socketChannel.write(IOUtil.obj2ByteBuffer(msg));</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;<span class="keyword">else</span> <span class="keyword">if</span>(msg.getType() == <span class="number">3</span>)&#123;<span class="comment">//向所有用户发送下线信息</span></span><br><span class="line">            msg.setData(String.valueOf(sk.attachment()));<span class="comment">//指定是谁下线了</span></span><br><span class="line">            Iterator&lt;Map.Entry&lt;String, SelectionKey&gt;&gt; iterator = nameChannel.entrySet().iterator();</span><br><span class="line">            <span class="keyword">while</span> (iterator.hasNext())&#123;</span><br><span class="line">                SocketChannel socketChannel = (SocketChannel)(iterator.next().getValue().channel());</span><br><span class="line">                socketChannel.write(IOUtil.obj2ByteBuffer(msg));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;<span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　到这里，我们就完成了服务端的调整说明，客户端的调整与服务端类似，主要调整了两处：1.控制台输入信息的解析 2.收到服务端的消息后的解析，具体代码请参考源码。注意：控制台信息输入格式请直接参考客户端源码中的parseScannerInput方法和Message类中type的详细说明，这里不再做介绍。</p>
<h2 id="u603B_u7ED3"><a href="#u603B_u7ED3" class="headerlink" title="总结"></a>总结</h2><p>　　对代码进行调整后，我们实现了消息的全局发送和指定用户发送，序列化方式采用jdk自带的。该代码仍待优化项包括：<br>　　1. 序列化方式可以采用protobuf等<br>　　2. 增加消息头，存入消息的长度，解决粘包的问题<br>　　3. 增加线程池，使服务端的parseMsg方法在新的线程中运行，避免主线程的阻塞<br>　　4. 至于更加完美的封装，请参考mina或者netty的模式。<br>源码下载地址：<a href="/code/channel-chat.zip">channel-chat.zip</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2015/12/23/nio/6.SocketChannel在线聊天/" data-id="cijzzpv6n004s1cfswwxf5nl4" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/socket/">socket</a></li></ul>

    </footer>
  </div>
  
</article>


  
  
    <nav id="page-nav">
      <a class="extend prev" rel="prev" href="/page/6/">&laquo; Prev</a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/5/">5</a><a class="page-number" href="/page/6/">6</a><span class="page-number current">7</span><a class="page-number" href="/page/8/">8</a><a class="page-number" href="/page/9/">9</a><span class="space">&hellip;</span><a class="page-number" href="/page/15/">15</a><a class="extend next" rel="next" href="/page/8/">Next &raquo;</a>
    </nav>
  
</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Categories</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/cache/">cache</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/database/">database</a><span class="category-list-count">13</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/docker/">docker</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/gis/">gis</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/git/">git</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/go/">go</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/gradle/">gradle</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/groovy/">groovy</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/hadoop/">hadoop</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/java/">java</a><span class="category-list-count">18</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/linux/">linux</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/log/">log</a><span class="category-list-count">11</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mongodb/">mongodb</a><span class="category-list-count">14</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mq/">mq</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mybatis/">mybatis</a><span class="category-list-count">9</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/netty/">netty</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nginx/">nginx</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nio/">nio</a><span class="category-list-count">19</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nodejs/">nodejs</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/redis/">redis</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/rpc/">rpc</a><span class="category-list-count">14</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/spring/">spring</a><span class="category-list-count">5</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/tomcat/">tomcat</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/zookeeper/">zookeeper</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/杂项/">杂项</a><span class="category-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/aop/">aop</a><span class="tag-list-count">8</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/aspectj/">aspectj</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cache/">cache</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/classloader/">classloader</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/common-loggings/">common-loggings</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/commonjs/">commonjs</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/database/">database</a><span class="tag-list-count">10</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/">docker</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/dubbo/">dubbo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/fastdfs/">fastdfs</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gclib/">gclib</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/geohash/">geohash</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gis/">gis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/go/">go</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gradle/">gradle</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/groovy/">groovy</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hadoop/">hadoop</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hessisan/">hessisan</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/java/">java</a><span class="tag-list-count">48</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/javassist/">javassist</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/kafka/">kafka</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linux/">linux</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/log/">log</a><span class="tag-list-count">11</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/log4j/">log4j</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/logback/">logback</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/map/">map</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/memecached/">memecached</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mina/">mina</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mongodb/">mongodb</a><span class="tag-list-count">14</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mq/">mq</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mqtt/">mqtt</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mybatis/">mybatis</a><span class="tag-list-count">9</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/netty/">netty</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/">nginx</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nio/">nio</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nodejs/">nodejs</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/postgresql/">postgresql</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/protobuf/">protobuf</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/proxy/">proxy</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/push/">push</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/rabbitmq/">rabbitmq</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/redis/">redis</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/rpc/">rpc</a><span class="tag-list-count">14</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sitemesh/">sitemesh</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sld/">sld</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/slf4j/">slf4j</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/socket/">socket</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spatialite-for-android/">spatialite for android</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spi/">spi</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring/">spring</a><span class="tag-list-count">13</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/tomcat/">tomcat</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/transaction/">transaction</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/web/">web</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/xSocket/">xSocket</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zbus/">zbus</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zookeeper/">zookeeper</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据结构/">数据结构</a><span class="tag-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/tags/aop/" style="font-size: 15.38px;">aop</a> <a href="/tags/aspectj/" style="font-size: 11.54px;">aspectj</a> <a href="/tags/cache/" style="font-size: 11.54px;">cache</a> <a href="/tags/classloader/" style="font-size: 10px;">classloader</a> <a href="/tags/common-loggings/" style="font-size: 10px;">common-loggings</a> <a href="/tags/commonjs/" style="font-size: 12.31px;">commonjs</a> <a href="/tags/database/" style="font-size: 16.92px;">database</a> <a href="/tags/docker/" style="font-size: 13.85px;">docker</a> <a href="/tags/dubbo/" style="font-size: 10px;">dubbo</a> <a href="/tags/fastdfs/" style="font-size: 10px;">fastdfs</a> <a href="/tags/gclib/" style="font-size: 10.77px;">gclib</a> <a href="/tags/geohash/" style="font-size: 10px;">geohash</a> <a href="/tags/gis/" style="font-size: 10px;">gis</a> <a href="/tags/git/" style="font-size: 10.77px;">git</a> <a href="/tags/go/" style="font-size: 10px;">go</a> <a href="/tags/gradle/" style="font-size: 10px;">gradle</a> <a href="/tags/groovy/" style="font-size: 10px;">groovy</a> <a href="/tags/hadoop/" style="font-size: 10px;">hadoop</a> <a href="/tags/hessisan/" style="font-size: 11.54px;">hessisan</a> <a href="/tags/java/" style="font-size: 20px;">java</a> <a href="/tags/javassist/" style="font-size: 10px;">javassist</a> <a href="/tags/kafka/" style="font-size: 11.54px;">kafka</a> <a href="/tags/linux/" style="font-size: 12.31px;">linux</a> <a href="/tags/log/" style="font-size: 17.69px;">log</a> <a href="/tags/log4j/" style="font-size: 12.31px;">log4j</a> <a href="/tags/logback/" style="font-size: 13.08px;">logback</a> <a href="/tags/map/" style="font-size: 10px;">map</a> <a href="/tags/memecached/" style="font-size: 10.77px;">memecached</a> <a href="/tags/mina/" style="font-size: 13.08px;">mina</a> <a href="/tags/mongodb/" style="font-size: 19.23px;">mongodb</a> <a href="/tags/mq/" style="font-size: 13.85px;">mq</a> <a href="/tags/mqtt/" style="font-size: 10.77px;">mqtt</a> <a href="/tags/mybatis/" style="font-size: 16.15px;">mybatis</a> <a href="/tags/netty/" style="font-size: 10px;">netty</a> <a href="/tags/nginx/" style="font-size: 11.54px;">nginx</a> <a href="/tags/nio/" style="font-size: 13.08px;">nio</a> <a href="/tags/nodejs/" style="font-size: 10px;">nodejs</a> <a href="/tags/postgresql/" style="font-size: 10.77px;">postgresql</a> <a href="/tags/protobuf/" style="font-size: 10px;">protobuf</a> <a href="/tags/proxy/" style="font-size: 10px;">proxy</a> <a href="/tags/push/" style="font-size: 10px;">push</a> <a href="/tags/rabbitmq/" style="font-size: 11.54px;">rabbitmq</a> <a href="/tags/redis/" style="font-size: 13.85px;">redis</a> <a href="/tags/rpc/" style="font-size: 19.23px;">rpc</a> <a href="/tags/sitemesh/" style="font-size: 10px;">sitemesh</a> <a href="/tags/sld/" style="font-size: 10px;">sld</a> <a href="/tags/slf4j/" style="font-size: 10px;">slf4j</a> <a href="/tags/socket/" style="font-size: 14.62px;">socket</a> <a href="/tags/spatialite-for-android/" style="font-size: 10px;">spatialite for android</a> <a href="/tags/spi/" style="font-size: 10px;">spi</a> <a href="/tags/spring/" style="font-size: 18.46px;">spring</a> <a href="/tags/tomcat/" style="font-size: 10.77px;">tomcat</a> <a href="/tags/transaction/" style="font-size: 10px;">transaction</a> <a href="/tags/web/" style="font-size: 10px;">web</a> <a href="/tags/xSocket/" style="font-size: 14.62px;">xSocket</a> <a href="/tags/zbus/" style="font-size: 10px;">zbus</a> <a href="/tags/zookeeper/" style="font-size: 10.77px;">zookeeper</a> <a href="/tags/数据结构/" style="font-size: 10.77px;">数据结构</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2016/01/">January 2016</a><span class="archive-list-count">67</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2015/12/">December 2015</a><span class="archive-list-count">81</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2015/11/">November 2015</a><span class="archive-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recents</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2016/01/30/资料/">(no title)</a>
          </li>
        
          <li>
            <a href="/2016/01/30/java/13.Classloader系列一/">ClassLoader系列一</a>
          </li>
        
          <li>
            <a href="/2016/01/29/hadoop/1.hadoop系列之源码编译/">hadoop系列一之源码编译</a>
          </li>
        
          <li>
            <a href="/2016/01/29/go/1.GO系列之入门/">Go系列一之入门</a>
          </li>
        
          <li>
            <a href="/2016/01/28/tomcat/2.tomcat性能优化/">tomcat性能优化</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2016 John Doe<br>
      Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="http://libs.baidu.com/jquery/2.0.3/jquery.min.js"></script>


  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css" type="text/css">
  <script src="/fancybox/jquery.fancybox.pack.js" type="text/javascript"></script>


<script src="/js/script.js" type="text/javascript"></script>

  </div>
</body>
</html>